{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"codehealth","informationUri":"https://codehealth.canine.dev","rules":[{"id":"D1","name":"Cyclomatic Complexity","shortDescription":{"text":"Cyclomatic Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D1"},{"id":"D2","name":"Cognitive Complexity","shortDescription":{"text":"Cognitive Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D2"},{"id":"D3","name":"God Classes","shortDescription":{"text":"God Classes"},"helpUri":"https://codehealth.canine.dev/dimensions/D3"},{"id":"D4","name":"Code Duplication","shortDescription":{"text":"Code Duplication"},"helpUri":"https://codehealth.canine.dev/dimensions/D4"},{"id":"D6","name":"Cohesion (LCOM4)","shortDescription":{"text":"Cohesion (LCOM4)"},"helpUri":"https://codehealth.canine.dev/dimensions/D6"},{"id":"D8","name":"Code Coverage","shortDescription":{"text":"Code Coverage"},"helpUri":"https://codehealth.canine.dev/dimensions/D8"},{"id":"D9","name":"Test Distribution","shortDescription":{"text":"Test Distribution"},"helpUri":"https://codehealth.canine.dev/dimensions/D9"},{"id":"D10","name":"Test Quality","shortDescription":{"text":"Test Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D10"},{"id":"D11","name":"Test Reliability","shortDescription":{"text":"Test Reliability"},"helpUri":"https://codehealth.canine.dev/dimensions/D11"},{"id":"D12","name":"Dependency Hygiene","shortDescription":{"text":"Dependency Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/D12"},{"id":"D13","name":"Secret Scanning","shortDescription":{"text":"Secret Scanning"},"helpUri":"https://codehealth.canine.dev/dimensions/D13","relationships":[{"target":{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"]}},{"id":"D14","name":"License Compliance","shortDescription":{"text":"License Compliance"},"helpUri":"https://codehealth.canine.dev/dimensions/D14"},{"id":"D15","name":"Churn \u00D7 Complexity Hotspots","shortDescription":{"text":"Churn \u00D7 Complexity Hotspots"},"helpUri":"https://codehealth.canine.dev/dimensions/D15"},{"id":"D16","name":"Bus Factor","shortDescription":{"text":"Bus Factor"},"helpUri":"https://codehealth.canine.dev/dimensions/D16"},{"id":"D17","name":"Explicit Debt","shortDescription":{"text":"Explicit Debt"},"helpUri":"https://codehealth.canine.dev/dimensions/D17"},{"id":"D19","name":"Documentation Quality","shortDescription":{"text":"Documentation Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D19"},{"id":"D20","name":"ADR Quality","shortDescription":{"text":"ADR Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D20"},{"id":"D21","name":"Naming Consistency","shortDescription":{"text":"Naming Consistency"},"helpUri":"https://codehealth.canine.dev/dimensions/D21"},{"id":"D26","name":"Project Cohesion","shortDescription":{"text":"Project Cohesion"},"helpUri":"https://codehealth.canine.dev/dimensions/D26"},{"id":"D28","name":"Secrets (history)","shortDescription":{"text":"Secrets (history)"},"helpUri":"https://codehealth.canine.dev/dimensions/D28","relationships":[{"target":{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"]}},{"id":"D29","name":"Static Analysis (SAST)","shortDescription":{"text":"Static Analysis (SAST)"},"helpUri":"https://codehealth.canine.dev/dimensions/D29","relationships":[{"target":{"id":"CWE-79","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-89","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-78","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-94","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-77","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-79","CWE-89","CWE-78","CWE-94","CWE-77"]}},{"id":"D30","name":"Dependency Vulnerabilities","shortDescription":{"text":"Dependency Vulnerabilities"},"helpUri":"https://codehealth.canine.dev/dimensions/D30","relationships":[{"target":{"id":"CWE-1395","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-937","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1395","CWE-937"]}},{"id":"D34","name":"Knowledge Freshness","shortDescription":{"text":"Knowledge Freshness"},"helpUri":"https://codehealth.canine.dev/dimensions/D34"},{"id":"D35","name":"Change Coupling","shortDescription":{"text":"Change Coupling"},"helpUri":"https://codehealth.canine.dev/dimensions/D35"},{"id":"D36","name":"Supply-chain Provenance \u0026 Signing","shortDescription":{"text":"Supply-chain Provenance \u0026 Signing"},"helpUri":"https://codehealth.canine.dev/dimensions/D36","relationships":[{"target":{"id":"CWE-1357","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]},{"target":{"id":"CWE-494","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1357","CWE-494"]}},{"id":"D43","name":"Malicious Dependencies","shortDescription":{"text":"Malicious Dependencies"},"helpUri":"https://codehealth.canine.dev/dimensions/D43","relationships":[{"target":{"id":"CWE-506","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-506"]}},{"id":"D44","name":"Platform End-of-Life","shortDescription":{"text":"Platform End-of-Life"},"helpUri":"https://codehealth.canine.dev/dimensions/D44"},{"id":"AX10","name":"Code composition","shortDescription":{"text":"Code composition"},"helpUri":"https://codehealth.canine.dev/dimensions/AX10"},{"id":"AX3","name":"Project dependency cycles","shortDescription":{"text":"Project dependency cycles"},"helpUri":"https://codehealth.canine.dev/dimensions/AX3"},{"id":"AX4","name":"Dependency direction","shortDescription":{"text":"Dependency direction"},"helpUri":"https://codehealth.canine.dev/dimensions/AX4"},{"id":"AX8","name":"Test isolation","shortDescription":{"text":"Test isolation"},"helpUri":"https://codehealth.canine.dev/dimensions/AX8"},{"id":"AX9","name":"CQS / query purity","shortDescription":{"text":"CQS / query purity"},"helpUri":"https://codehealth.canine.dev/dimensions/AX9"},{"id":"DM4","name":"Rich vs anemic domain model","shortDescription":{"text":"Rich vs anemic domain model"},"helpUri":"https://codehealth.canine.dev/dimensions/DM4"},{"id":"DM5","name":"Encapsulated state","shortDescription":{"text":"Encapsulated state"},"helpUri":"https://codehealth.canine.dev/dimensions/DM5"},{"id":"DM6","name":"Domain \u2194 infrastructure boundary","shortDescription":{"text":"Domain \u2194 infrastructure boundary"},"helpUri":"https://codehealth.canine.dev/dimensions/DM6"},{"id":"M1","name":"Documentation (README)","shortDescription":{"text":"Documentation (README)"},"helpUri":"https://codehealth.canine.dev/dimensions/M1"},{"id":"M2","name":"Architecture documentation","shortDescription":{"text":"Architecture documentation"},"helpUri":"https://codehealth.canine.dev/dimensions/M2"},{"id":"M3","name":"Folder \u0026 project structure","shortDescription":{"text":"Folder \u0026 project structure"},"helpUri":"https://codehealth.canine.dev/dimensions/M3"},{"id":"M4","name":"Documentation accuracy","shortDescription":{"text":"Documentation accuracy"},"helpUri":"https://codehealth.canine.dev/dimensions/M4"},{"id":"P1","name":"CI/CD gates","shortDescription":{"text":"CI/CD gates"},"helpUri":"https://codehealth.canine.dev/dimensions/P1"},{"id":"P12","name":"CI test-gate honesty","shortDescription":{"text":"CI test-gate honesty"},"helpUri":"https://codehealth.canine.dev/dimensions/P12"},{"id":"P2","name":"Observability","shortDescription":{"text":"Observability"},"helpUri":"https://codehealth.canine.dev/dimensions/P2"},{"id":"P3","name":"Security \u0026 performance tooling","shortDescription":{"text":"Security \u0026 performance tooling"},"helpUri":"https://codehealth.canine.dev/dimensions/P3"},{"id":"P4","name":"Deployment \u0026 Rollback","shortDescription":{"text":"Deployment \u0026 Rollback"},"helpUri":"https://codehealth.canine.dev/dimensions/P4"},{"id":"P6","name":"Release Hygiene","shortDescription":{"text":"Release Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/P6"},{"id":"PF2","name":"Allocation hygiene","shortDescription":{"text":"Allocation hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/PF2"},{"id":"X10","name":"Duplicated predicate","shortDescription":{"text":"Duplicated predicate"},"helpUri":"https://codehealth.canine.dev/dimensions/X10"},{"id":"X32","name":"Type resolved by simple name across every loaded assembly","shortDescription":{"text":"Type resolved by simple name across every loaded assembly"},"helpUri":"https://codehealth.canine.dev/dimensions/X32"},{"id":"X6","name":"Hand-rolled structured-format parsing","shortDescription":{"text":"Hand-rolled structured-format parsing"},"helpUri":"https://codehealth.canine.dev/dimensions/X6"},{"id":"X9","name":"Subsumed condition operand","shortDescription":{"text":"Subsumed condition operand"},"helpUri":"https://codehealth.canine.dev/dimensions/X9"}]}},"results":[{"ruleId":"D1","level":"warning","message":{"text":"PowerGameStateParser.handle (cyclomatic 636): PowerGameStateParser.handle has cyclomatic complexity 636 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"46a7dc546f947c8712284e0fab4be387719e0fdd5d36e72c96f5cd502100e0d6"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.setupInputPlayer (cyclomatic 75): BobsBuddyInvoker.setupInputPlayer has cyclomatic complexity 75 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1171}}}],"partialFingerprints":{"codehealthFindingId/v1":"724f6d3d79369256e753c86634549fe42e9d772ecb41bbe88ab0bcf34e6a15e5"}},{"ruleId":"D1","level":"warning","message":{"text":"ArenaVectorPath.path (cyclomatic 51): ArenaVectorPath.path has cyclomatic complexity 51 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaVectorIcons.swift"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"2dd688f90038d984424478b3810dd5656dc8e47350d45486c0ecb09bb6785946"}},{"ruleId":"D1","level":"warning","message":{"text":"Database.apply (cyclomatic 49): Database.apply has cyclomatic complexity 49 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Database.swift"},"region":{"startLine":167}}}],"partialFingerprints":{"codehealthFindingId/v1":"29ce4fccc7e9e1c8131c86485b823c04aa9671fcb023d8ca5200d67aa6ea94fe"}},{"ruleId":"D1","level":"warning","message":{"text":"CardBar.draw (cyclomatic 47): CardBar.draw has cyclomatic complexity 47 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":297}}}],"partialFingerprints":{"codehealthFindingId/v1":"7b267bbfda3bbb69b45322e5a6d258781bbf6abff45cefb5af8cdffc5e1b9eb7"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.getMinionFromEntity (cyclomatic 46): BobsBuddyInvoker.getMinionFromEntity has cyclomatic complexity 46 (threshold 15). To reduce it, separate the branches: extract each independent case into its own named function so the top-level body reads as a short sequence of named decisions."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":781}}}],"partialFingerprints":{"codehealthFindingId/v1":"379418da0febfc56a092bec1cc992d8fcd245f684cef13ad8bbe176d8572f4f9"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.onCardCopy (cyclomatic 43): TagChangeActions.onCardCopy has cyclomatic complexity 43 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":281}}}],"partialFingerprints":{"codehealthFindingId/v1":"ed46db095dcbd958a845a6ad72cc0be7f48abf2541d89e7644c98469215c7296"}},{"ruleId":"D1","level":"warning","message":{"text":"TrackerPanelLayout.init (cyclomatic 43): TrackerPanelLayout.init has cyclomatic complexity 43 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Trackers/TrackerPanelView.swift"},"region":{"startLine":336}}}],"partialFingerprints":{"codehealthFindingId/v1":"b6a122270ddf132fc9be1073dc84aa6d09a8f9325fc4e3e4dd60b6cf04ca560d"}},{"ruleId":"D1","level":"warning","message":{"text":"CoreManager.updateDungeonRunDeck (cyclomatic 42): CoreManager.updateDungeonRunDeck has cyclomatic complexity 42 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":664}}}],"partialFingerprints":{"codehealthFindingId/v1":"bfc22024899a3912f1b3f3f50359d5e4f9896047377bab457abef2dd5db82508"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.generateEndgameStatistics (cyclomatic 41): Game.generateEndgameStatistics has cyclomatic complexity 41 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2158}}}],"partialFingerprints":{"codehealthFindingId/v1":"476329a1bbfac21bcaa95b651b34ff6bb4bc41ee6e72b78104e658607b23e228"}},{"ruleId":"D1","level":"warning","message":{"text":"LoadingScreenHandler.handle (cyclomatic 41): LoadingScreenHandler.handle has cyclomatic complexity 41 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/LoadingScreenHandler.swift"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"af7a60f1d89ca6580a2117e3b149b432e8a272faf1382174fadbbe7eafa52476"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.syncStats (cyclomatic 40): Game.syncStats has cyclomatic complexity 40 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2458}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d210267e8240f087e7142e695ae62fce82c52a765977c7b8b85eea428caa9c9"}},{"ruleId":"D1","level":"warning","message":{"text":"BattlegroundsPreferences.checkboxClicked (cyclomatic 39): BattlegroundsPreferences.checkboxClicked has cyclomatic complexity 39 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/BattlegroundsPreferences.swift"},"region":{"startLine":106}}}],"partialFingerprints":{"codehealthFindingId/v1":"3362ae02fe2f75f391f8ef0707c309d74ee4369b4259b5a4b58be1bedd2c659c"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.tooltipDisplay (cyclomatic 37): Game.tooltipDisplay has cyclomatic complexity 37 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5105}}}],"partialFingerprints":{"codehealthFindingId/v1":"78972c08bcad929f96854b425d3d6739667118bb5bfcad6bd9ae44be836f016c"}},{"ruleId":"D1","level":"warning","message":{"text":"Player.opponentCardList (cyclomatic 37): Player.opponentCardList has cyclomatic complexity 37 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":560}}}],"partialFingerprints":{"codehealthFindingId/v1":"15f4069adab66145d425d07a2bc3787cfee28bafda591a561b2df0fa94f3df2a"}},{"ruleId":"D1","level":"warning","message":{"text":"UploadMetaData.getPlayerInfo (cyclomatic 37): UploadMetaData.getPlayerInfo has cyclomatic complexity 37 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/Data/UploadMetaData.swift"},"region":{"startLine":179}}}],"partialFingerprints":{"codehealthFindingId/v1":"5101c895754f4296d520b912607a094b3bc54d50b897b3cbad2c9c114ddb2dbf"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromDeck (cyclomatic 36): TagChangeActions.zoneChangeFromDeck has cyclomatic complexity 36 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1411}}}],"partialFingerprints":{"codehealthFindingId/v1":"20b7fceed0c09ebb11816a74b7a81987d9d98a0cfa67f1ffbac18f22b3c34f70"}},{"ruleId":"D1","level":"warning","message":{"text":"BattlegroundsPreferences.viewWillAppear (cyclomatic 36): BattlegroundsPreferences.viewWillAppear has cyclomatic complexity 36 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/BattlegroundsPreferences.swift"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"8ddb8e9698542b58579b50c3828f5d800204d00c47d3e777d00af6322909a496"}},{"ruleId":"D1","level":"warning","message":{"text":"DungeonRunDeckWatcher.update (cyclomatic 35): DungeonRunDeckWatcher.update has cyclomatic complexity 35 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/DungeonRunWatcher.swift"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"78d46668747a1426275fdf5c72c3fc08e9b924c6797d4f81ff4869a7d04fa7cc"}},{"ruleId":"D1","level":"warning","message":{"text":"Player.getDeckState (cyclomatic 35): Player.getDeckState has cyclomatic complexity 35 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":627}}}],"partialFingerprints":{"codehealthFindingId/v1":"cccd76860cc8a1275851e624dd5f812c2a7e3db6272c4e4879a6ccfe441ee861"}},{"ruleId":"D1","level":"warning","message":{"text":"BnetGameType.getBnetGameType (cyclomatic 33): BnetGameType.getBnetGameType has cyclomatic complexity 33 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Enums/BnetGameType.swift"},"region":{"startLine":54}}}],"partialFingerprints":{"codehealthFindingId/v1":"af56e9311d65eba51b39f687017fa7d79555d967ebdfa75c7cbdab345b8cf5cb"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.zoneChange (cyclomatic 33): TagChangeActions.zoneChange has cyclomatic complexity 33 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1015}}}],"partialFingerprints":{"codehealthFindingId/v1":"7ca22b4bc47892a8eeed9d57df197002d425ffc9809ed42f2f019af05ee2a374"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromOther (cyclomatic 33): TagChangeActions.zoneChangeFromOther has cyclomatic complexity 33 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1146}}}],"partialFingerprints":{"codehealthFindingId/v1":"357f7c75b6e17743579893679ee818e8ba725c32b7c80e918ac8bb149368de20"}},{"ruleId":"D1","level":"warning","message":{"text":"ArenaWatcher.update (cyclomatic 32): ArenaWatcher.update has cyclomatic complexity 32 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/ArenaWatcher.swift"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"9f8e928d4f8751c9ddfea666cd64be8c0fa8074239be31db89655884b6e2ab14"}},{"ruleId":"D1","level":"warning","message":{"text":"RootOverlayViewModel.setCardOpacityMask (cyclomatic 32): RootOverlayViewModel.setCardOpacityMask has cyclomatic complexity 32 (threshold 15). To reduce it, separate the branches: extract each independent case into its own named function so the top-level body reads as a short sequence of named decisions."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift"},"region":{"startLine":137}}}],"partialFingerprints":{"codehealthFindingId/v1":"2e21042b08cc14c7edb42e57508c0cd975c405f2af5871818d2db6f3a7eb23ac"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.setRelatedCardsTrigger (cyclomatic 31): Game.setRelatedCardsTrigger has cyclomatic complexity 31 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5287}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b8175bcc7912b58e7198afcfcb5b30f5e6cb655f3532d4707eb252f5fed8241"}},{"ruleId":"D1","level":"warning","message":{"text":"BattlegroundsDb.update (cyclomatic 31): BattlegroundsDb.update has cyclomatic complexity 31 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BattlegroundsDb.swift"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"c10faa260b1776e4b9ffeddcf0bb133acf77ef709ed7cacba69664a0dc93dcba"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromHand (cyclomatic 31): TagChangeActions.zoneChangeFromHand has cyclomatic complexity 31 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1321}}}],"partialFingerprints":{"codehealthFindingId/v1":"919458a491602573caf40955a62fc957c96d251beb101be474aa7310c218db20"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.runSimulation (cyclomatic 29): BobsBuddyInvoker.runSimulation has cyclomatic complexity 29 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":369}}}],"partialFingerprints":{"codehealthFindingId/v1":"cd55019176d45c4483b68bce259557c335c543195959fe6b09ebae71eb4a8d12"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.updateOpponentTracker (cyclomatic 29): Game.updateOpponentTracker has cyclomatic complexity 29 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":287}}}],"partialFingerprints":{"codehealthFindingId/v1":"e3c2f5b45fbd3a14e4a29f96960c6db06dc652598e432418d70a27069159718e"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.handleHearthstoneMulliganPhase (cyclomatic 29): Game.handleHearthstoneMulliganPhase has cyclomatic complexity 29 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4580}}}],"partialFingerprints":{"codehealthFindingId/v1":"32e0f6059fe9d662b084714911dbd08443c50caf52e4b9096b9a557448946f56"}},{"ruleId":"D1","level":"warning","message":{"text":"ArenaStateWatcher.updateCore (cyclomatic 29): ArenaStateWatcher.updateCore has cyclomatic complexity 29 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/Arena/ArenaStateWatcher.swift"},"region":{"startLine":196}}}],"partialFingerprints":{"codehealthFindingId/v1":"53f611b57da15807853fbc853dcce2e22fd6fec9c799b92c21c36adf2427acb0"}},{"ruleId":"D1","level":"warning","message":{"text":"TrackersPreferences.checkboxClicked (cyclomatic 29): TrackersPreferences.checkboxClicked has cyclomatic complexity 29 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/TrackersPreferences.swift"},"region":{"startLine":109}}}],"partialFingerprints":{"codehealthFindingId/v1":"422175504128fece27b6c4a2dd5021d298e50b2f7248b562df485153d355dce4"}},{"ruleId":"D1","level":"warning","message":{"text":"CoreManager.setup (cyclomatic 28): CoreManager.setup has cyclomatic complexity 28 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":136}}}],"partialFingerprints":{"codehealthFindingId/v1":"5487f006bb9fd1783f1ebef2fb2eb7d05e51c6c6bafd9048f240fe6c67c1e0d5"}},{"ruleId":"D1","level":"warning","message":{"text":"TrackersPreferences.viewWillAppear (cyclomatic 28): TrackersPreferences.viewWillAppear has cyclomatic complexity 28 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/TrackersPreferences.swift"},"region":{"startLine":45}}}],"partialFingerprints":{"codehealthFindingId/v1":"df926f888151f8dfe1d6da172107f0445994239e369b652223070e9a5afa818d"}},{"ruleId":"D1","level":"warning","message":{"text":"CardUtils.isAllowedInGenerationPool (cyclomatic 26): CardUtils.isAllowedInGenerationPool has cyclomatic complexity 26 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CardUtils.swift"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"7ebd79059761503b8bc8d9f3f007e369c07e55954415d830a615e60ad0c103d0"}},{"ruleId":"D1","level":"warning","message":{"text":"CoreManager.dungeonRunMatchStarted (cyclomatic 26): CoreManager.dungeonRunMatchStarted has cyclomatic complexity 26 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":581}}}],"partialFingerprints":{"codehealthFindingId/v1":"677c96f59f52f086f4e89b97c70b64f927fa4e62ce4eadb2b826cafb16912250"}},{"ruleId":"D1","level":"warning","message":{"text":"ImageUtils.loadImage (cyclomatic 26): ImageUtils.loadImage has cyclomatic complexity 26 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/ImageUtils.swift"},"region":{"startLine":143}}}],"partialFingerprints":{"codehealthFindingId/v1":"2308359209217492e45e0c26f66e6a1f71b62ac0e610fe07adae3eccade4132a"}},{"ruleId":"D1","level":"warning","message":{"text":"DungeonRun.getCards (cyclomatic 26): DungeonRun.getCards has cyclomatic complexity 26 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CardIds/DefaultDecks.swift"},"region":{"startLine":89}}}],"partialFingerprints":{"codehealthFindingId/v1":"2675e97a8bd959e84d2b68b7cf6f73bca508fc9ce1f7dd8bb349ae2e6b21f6f4"}},{"ruleId":"D1","level":"warning","message":{"text":"StatusMessageConverter.getStatusMessage (cyclomatic 25): StatusMessageConverter.getStatusMessage has cyclomatic complexity 25 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/StatusMessageConverter.swift"},"region":{"startLine":12}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c18c79404cd1f8b3e96bcc57fd28a9b088af217cc8b4a2b9786703d29db9b43"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.updatePlayerTracker (cyclomatic 24): Game.updatePlayerTracker has cyclomatic complexity 24 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":382}}}],"partialFingerprints":{"codehealthFindingId/v1":"8aba026482bb5d91651e41afe581b4eea6673cb1762e1b01b88bef57fbc4cd91"}},{"ruleId":"D1","level":"warning","message":{"text":"SecretsManager.handleAttack (cyclomatic 24): SecretsManager.handleAttack has cyclomatic complexity 24 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":458}}}],"partialFingerprints":{"codehealthFindingId/v1":"20a1a4f744a98064495758139c0c276c5520d7dad7cc1e99df18cb8a7d042fe4"}},{"ruleId":"D1","level":"warning","message":{"text":"SecretsManager.handleCardPlayed (cyclomatic 24): SecretsManager.handleCardPlayed has cyclomatic complexity 24 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":791}}}],"partialFingerprints":{"codehealthFindingId/v1":"2162a36f4d9b651352192e959eb0cf8afb81be07125941e1fec3788bdab1e989"}},{"ruleId":"D1","level":"warning","message":{"text":"ChoicesHandler.handle (cyclomatic 24): ChoicesHandler.handle has cyclomatic complexity 24 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/ChoicesHandler.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"384623cdf956cd1177a6302c12d002d70b601b25b13612a2fc6396c37fdd1471"}},{"ruleId":"D1","level":"warning","message":{"text":"AppDelegate.applicationDidFinishLaunching (cyclomatic 23): AppDelegate.applicationDidFinishLaunching has cyclomatic complexity 23 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":82}}}],"partialFingerprints":{"codehealthFindingId/v1":"6b73da1565709380f3be82901ced65e020e419ceb5b301268d21b0bc069de60a"}},{"ruleId":"D1","level":"warning","message":{"text":"LogUploader.upload (cyclomatic 23): LogUploader.upload has cyclomatic complexity 23 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/LogUploader.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"2235efdf9d6979f254af4447f210e3395ffe9a3085decc20d435ebd5719453a4"}},{"ruleId":"D1","level":"warning","message":{"text":"LogReader.readFile (cyclomatic 23): LogReader.readFile has cyclomatic complexity 23 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/LogReader.swift"},"region":{"startLine":92}}}],"partialFingerprints":{"codehealthFindingId/v1":"22f2afbdb1cb7d2af10b456293e11b2d997312501c0a4d727a4095ecdf42a476"}},{"ruleId":"D1","level":"warning","message":{"text":"Player.play (cyclomatic 23): Player.play has cyclomatic complexity 23 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":893}}}],"partialFingerprints":{"codehealthFindingId/v1":"af064b42fcf4241ff7ad554bbcbc798f53ec9ffa561099d000a05f1472c92257"}},{"ruleId":"D1","level":"warning","message":{"text":"BattlegroundsMinionView.draw (cyclomatic 23): BattlegroundsMinionView.draw has cyclomatic complexity 23 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BattlegroundsMinionView.swift"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"707a400283e0ff0ecfd646bdd7decb7bfc2a47bdf370b3d12be3d1f10a5ce8c0"}},{"ruleId":"D1","level":"warning","message":{"text":"CardTooltipPanel.show (cyclomatic 23): CardTooltipPanel.show has cyclomatic complexity 23 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift"},"region":{"startLine":305}}}],"partialFingerprints":{"codehealthFindingId/v1":"2403a7d5a7f36a8b086379c4ca8de5a32b5042af721fd40bb2c015f14f1dc952"}},{"ruleId":"D1","level":"warning","message":{"text":"Player.getOpponentDeckState (cyclomatic 22): Player.getOpponentDeckState has cyclomatic complexity 22 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":761}}}],"partialFingerprints":{"codehealthFindingId/v1":"a452ec0e803ea0ff8f1d6c50e316be451664a2853b4682cf25a8b8d1347af159"}},{"ruleId":"D1","level":"warning","message":{"text":"PlayerTrackersPreferences.viewWillAppear (cyclomatic 22): PlayerTrackersPreferences.viewWillAppear has cyclomatic complexity 22 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/PlayerTrackersPreferences.swift"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"154c83d94498561d1a59c193471a6e852e3de58cc5b1f9ca0181e6feba1a572f"}},{"ruleId":"D1","level":"warning","message":{"text":"PlayerTrackersPreferences.checkboxClicked (cyclomatic 22): PlayerTrackersPreferences.checkboxClicked has cyclomatic complexity 22 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/PlayerTrackersPreferences.swift"},"region":{"startLine":77}}}],"partialFingerprints":{"codehealthFindingId/v1":"856ae7fb511d191462899806dbb55e92a7fdf6b7872f27dd8a7af46aafe8aea3"}},{"ruleId":"D1","level":"warning","message":{"text":"AnimalCompanionCounter.handleTagChange (cyclomatic 22): AnimalCompanionCounter.handleTagChange has cyclomatic complexity 22 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AnimalCompanionCounter.swift"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"4515b148b98b3caf8a3ffbc041936c948cbe84878918afad71d67d3eb873d608"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.gameStart (cyclomatic 21): Game.gameStart has cyclomatic complexity 21 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":1939}}}],"partialFingerprints":{"codehealthFindingId/v1":"07d0bc5c382d611e0bcd609c79157955c66f7f2443c1a537c0bafc462bd25d1a"}},{"ruleId":"D1","level":"warning","message":{"text":"UploadMetaData.generate (cyclomatic 21): UploadMetaData.generate has cyclomatic complexity 21 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/Data/UploadMetaData.swift"},"region":{"startLine":85}}}],"partialFingerprints":{"codehealthFindingId/v1":"447eb7cb9e3d022b36f7aac1cf012af93ee56fbe7413d8b9d269a3fea6a1ee9d"}},{"ruleId":"D1","level":"warning","message":{"text":"AnimatedCardList.update (cyclomatic 21): AnimatedCardList.update has cyclomatic complexity 21 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Trackers/AnimatedCardList.swift"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"b9d88e96f2e34da1e44aaf31f3b36eb4a617c0ab1daba180248b7bc36dda467f"}},{"ruleId":"D1","level":"warning","message":{"text":"BoardMouseOverDetection.update (cyclomatic 21): BoardMouseOverDetection.update has cyclomatic complexity 21 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Board/BoardMouseOverDetection.swift"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"a70e8277d6b250468aedb513bfc6d59aa2b32c226a894e119547878827311c4b"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.handlePlayerMulliganDone (cyclomatic 20): Game.handlePlayerMulliganDone has cyclomatic complexity 20 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4021}}}],"partialFingerprints":{"codehealthFindingId/v1":"51eec523693ce6bcda564fbbc5c760c3e1bd452ab7bcec51400912905f28e6a4"}},{"ruleId":"D1","level":"warning","message":{"text":"OpponentTrackersPreferences.viewWillAppear (cyclomatic 20): OpponentTrackersPreferences.viewWillAppear has cyclomatic complexity 20 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/OpponentTrackersPreferences.swift"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"6ca59c33dd1e447e83383eeda6630c39ff6f7e0cafa140e77f75ef2483c229dd"}},{"ruleId":"D1","level":"warning","message":{"text":"OpponentTrackersPreferences.checkboxClicked (cyclomatic 20): OpponentTrackersPreferences.checkboxClicked has cyclomatic complexity 20 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/OpponentTrackersPreferences.swift"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"a66036e0dc7d503a9080d5a51cff46cb9665a09800eb4b24b5d62223de5ab18c"}},{"ruleId":"D1","level":"warning","message":{"text":"OpponentHandMarkersViewModel.update (cyclomatic 20): OpponentHandMarkersViewModel.update has cyclomatic complexity 20 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Hand/CardMarkerViewModel.swift"},"region":{"startLine":137}}}],"partialFingerprints":{"codehealthFindingId/v1":"ccee9489a601dd448a3212b5351498aaf86702ac4bb8d8ee2e72b77cff214248"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.checkForRepeatedMagnetizedAutoAssemblers (cyclomatic 19): BobsBuddyInvoker.checkForRepeatedMagnetizedAutoAssemblers has cyclomatic complexity 19 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":963}}}],"partialFingerprints":{"codehealthFindingId/v1":"65bcd967dbdb30400a19c9e157c9ea1c05960e68400bf73f74b49f2fbb6dd5b4"}},{"ruleId":"D1","level":"warning","message":{"text":"Cards.search (cyclomatic 19): Cards.search has cyclomatic complexity 19 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Cards.swift"},"region":{"startLine":158}}}],"partialFingerprints":{"codehealthFindingId/v1":"5f521fbf0934514c7bd985d4b070ae7596a949e0cec696bdd1dc8b28d49d4905"}},{"ruleId":"D1","level":"warning","message":{"text":"RealmHelper.checkAndUpdateDeck (cyclomatic 19): RealmHelper.checkAndUpdateDeck has cyclomatic complexity 19 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/RealmHelper.swift"},"region":{"startLine":180}}}],"partialFingerprints":{"codehealthFindingId/v1":"454ac51af32f049407a7677d6fbcc3148415dd805ff9b5fa696c64da6e7b2357"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.handleEndGame (cyclomatic 19): Game.handleEndGame has cyclomatic complexity 19 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2322}}}],"partialFingerprints":{"codehealthFindingId/v1":"b4a7d8020bf481050ba49867fb92e60fc5a9d6e539241b3bf7a90b5d37a5e20f"}},{"ruleId":"D1","level":"warning","message":{"text":"NotificationManager.showNotification (cyclomatic 19): NotificationManager.showNotification has cyclomatic complexity 19 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/NotificationManager.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"29067046fc2c08c6f24f1781f028fc837ac07f65d5bb6476fc1d910bd6046a69"}},{"ruleId":"D1","level":"warning","message":{"text":"SecretsManager.handleOpponentMinionDeath (cyclomatic 19): SecretsManager.handleOpponentMinionDeath has cyclomatic complexity 19 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":607}}}],"partialFingerprints":{"codehealthFindingId/v1":"61285c38644dcf721c7add8f460b5c97c20ffe5fe4c36035093e5cd276c0a2b4"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromPlay (cyclomatic 19): TagChangeActions.zoneChangeFromPlay has cyclomatic complexity 19 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1272}}}],"partialFingerprints":{"codehealthFindingId/v1":"3b04bf2d776a746e8cd61b6d986760ef4876bd766290ab1027070347eea8cef0"}},{"ruleId":"D1","level":"warning","message":{"text":"CurveView.draw (cyclomatic 19): CurveView.draw has cyclomatic complexity 19 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/CurveView.swift"},"region":{"startLine":52}}}],"partialFingerprints":{"codehealthFindingId/v1":"cffa694fb10c4125ee90844d97729f25a0c462736ccf189c3f17753acb96c8fc"}},{"ruleId":"D1","level":"warning","message":{"text":"GoldNextTurnCounter.handleTagChange (cyclomatic 19): GoldNextTurnCounter.handleTagChange has cyclomatic complexity 19 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/GoldNextTurnCounter.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"afc6b66200bb705c981dbd554bcf7064749f764b157469624f82abf5c0886296"}},{"ruleId":"D1","level":"warning","message":{"text":"AppDelegate.buildMenu (cyclomatic 18): AppDelegate.buildMenu has cyclomatic complexity 18 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":589}}}],"partialFingerprints":{"codehealthFindingId/v1":"98e9e64069fd0c2350ee9f8fc3951d72bb2b005aa1b81025943aeb65592204c3"}},{"ruleId":"D1","level":"warning","message":{"text":"DungeonRunDeckWatcher.updateDungeonInfo (cyclomatic 18): DungeonRunDeckWatcher.updateDungeonInfo has cyclomatic complexity 18 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/DungeonRunWatcher.swift"},"region":{"startLine":150}}}],"partialFingerprints":{"codehealthFindingId/v1":"7491957a705960c39113a08be039d25fd153a92456b5dc27fbc3f5af7660bf6a"}},{"ruleId":"D1","level":"warning","message":{"text":"EntityInfo.description (cyclomatic 18): EntityInfo.description has cyclomatic complexity 18 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/EntityInfo.swift"},"region":{"startLine":132}}}],"partialFingerprints":{"codehealthFindingId/v1":"2d5b5e222d383747caa614f2c65ca393fbee07e06d79343b46c1e537d2ea740a"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.linkedEntity (cyclomatic 18): TagChangeActions.linkedEntity has cyclomatic complexity 18 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":353}}}],"partialFingerprints":{"codehealthFindingId/v1":"32ada8f58b835e0022c774577ab0264527fd7d4443538bf08e9446ac89ee83f0"}},{"ruleId":"D1","level":"warning","message":{"text":"ArenaPickHelperViewModel.onDeckEditing (cyclomatic 18): ArenaPickHelperViewModel.onDeckEditing has cyclomatic complexity 18 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPickHelperViewModel.swift"},"region":{"startLine":641}}}],"partialFingerprints":{"codehealthFindingId/v1":"199baf55d1a5b111058d7516b83e95171e8c27727a15dfdc49381a0b3f01ac8a"}},{"ruleId":"D1","level":"warning","message":{"text":"ConstructedMulliganGuidePreLobbyViewModel.update (cyclomatic 18): ConstructedMulliganGuidePreLobbyViewModel.update has cyclomatic complexity 18 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift"},"region":{"startLine":430}}}],"partialFingerprints":{"codehealthFindingId/v1":"4b5dc3fbbebd4fab715577bb05b9eeae4a7f489af8c5b2909d3e15dc1eb26636"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.snapshotBoardState (cyclomatic 17): BobsBuddyInvoker.snapshotBoardState has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1519}}}],"partialFingerprints":{"codehealthFindingId/v1":"e9be3ece2049ba4d0d030259d78d8264e83352c4a99322a7d8e61c7e821a4955"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.reconcileAutoAssemblerDeathrattles (cyclomatic 17): BobsBuddyInvoker.reconcileAutoAssemblerDeathrattles has cyclomatic complexity 17 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":2244}}}],"partialFingerprints":{"codehealthFindingId/v1":"bcb10052c044c3768ab09724fac29737dec7b372879a915a9e2c5a587c1f7647"}},{"ruleId":"D1","level":"warning","message":{"text":"HearthstoneWindow.reload (cyclomatic 17): HearthstoneWindow.reload has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/SizeHelper.swift"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"37cac2ca513a895b20db5d71686d481bba3c456d65af0c0b129f1815b7a4ec7a"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.updateBoardStateTrackers (cyclomatic 17): Game.updateBoardStateTrackers has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":813}}}],"partialFingerprints":{"codehealthFindingId/v1":"ec670b94a7a4bfed589f5f93bb24fabeaf24d5feb0727730e98e69b30f2a277d"}},{"ruleId":"D1","level":"warning","message":{"text":"ModuleMetadata.init (cyclomatic 17): ModuleMetadata.init has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/ReflectionHelper.swift"},"region":{"startLine":166}}}],"partialFingerprints":{"codehealthFindingId/v1":"4556ceb0bdeae64fd82df8eef770d3b3a4264072ca0f731a208e03c135555fd4"}},{"ruleId":"D1","level":"warning","message":{"text":"RelatedCardsManager.tryGetBucketedRelatedCardsSummary (cyclomatic 17): RelatedCardsManager.tryGetBucketedRelatedCardsSummary has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":290}}}],"partialFingerprints":{"codehealthFindingId/v1":"981afff093a9e89cb41b64e75ca813737bce36366fcd6fdb37ef5b5348fadcb9"}},{"ruleId":"D1","level":"warning","message":{"text":"SecretsManager.getSecretsCreatedBy (cyclomatic 17): SecretsManager.getSecretsCreatedBy has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"4be8c2942e932eaffd54fff64d72ef96f6d979829275440d47d8a1ebab89246a"}},{"ruleId":"D1","level":"warning","message":{"text":"SecretsManager.handleTurnsInPlayChange (cyclomatic 17): SecretsManager.handleTurnsInPlayChange has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":731}}}],"partialFingerprints":{"codehealthFindingId/v1":"759cc844f7927799b2d48ea1de735293bed7d9fbec4559b8be64ef4f45207e0f"}},{"ruleId":"D1","level":"warning","message":{"text":"TagChangeActions.creatorChanged (cyclomatic 17): TagChangeActions.creatorChanged has cyclomatic complexity 17 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top. This is NOT this file\u0027s highest cyclomatic complexity: TagChangeActions.findAction (cyclomatic 58) is higher and carries no row of its own \u2014 it was excluded as a flat dispatcher (a long switch/match over independent cases: many branches, almost no nesting), which this dimension does not treat as a refactor obligation. It is named here so the ranking you see in this file is not mistaken for the whole of it; the excluded method is counted neither in this dimension\u0027s figures nor in its score."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":507}}}],"partialFingerprints":{"codehealthFindingId/v1":"68f5658dc3a9528703c9f3035c0dc69b34ceb1534323152ddf097be277a6cbab"}},{"ruleId":"D1","level":"warning","message":{"text":"CardBar.addCardImage (cyclomatic 17): CardBar.addCardImage has cyclomatic complexity 17 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":379}}}],"partialFingerprints":{"codehealthFindingId/v1":"478c1575d7e6adb3baf75aa56055185f162d2cacdb4ea3455534d4bfb7d36918"}},{"ruleId":"D1","level":"warning","message":{"text":"BoardCard.isAbleToAttack (cyclomatic 17): BoardCard.isAbleToAttack has cyclomatic complexity 17 (threshold 15). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BoardDamage/BoardCard.swift"},"region":{"startLine":113}}}],"partialFingerprints":{"codehealthFindingId/v1":"c8624d2b5b054e63f5dc94f7a5f1856cfd6fd66c609524f01c9480f509958380"}},{"ruleId":"D1","level":"warning","message":{"text":"BattlegroundsMinionArtView.body (cyclomatic 17): BattlegroundsMinionArtView.body has cyclomatic complexity 17 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/BattlegroundsMinionArtView.swift"},"region":{"startLine":126}}}],"partialFingerprints":{"codehealthFindingId/v1":"85e56b13c3ffd123f4c24106e1022ef5fb305de14a6e848c3e028659931695c6"}},{"ruleId":"D1","level":"warning","message":{"text":"CardTooltipPanel.placedFrame (cyclomatic 17): CardTooltipPanel.placedFrame has cyclomatic complexity 17 (threshold 15). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Where every arm is uniform \u2014 the same kind of value, with no behaviour of its own \u2014 a table keyed by the case is the shorter form; wherever the arms carry different data or different behaviour, keep them as cases, because collapsing those trades an explicit, reviewable set of cases for nothing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift"},"region":{"startLine":666}}}],"partialFingerprints":{"codehealthFindingId/v1":"57a93b4cef96d52fb3d5476808ee7f71fe8290b00edc4e2c88354cf0e5b6f131"}},{"ruleId":"D1","level":"warning","message":{"text":"BattlegroundsQuestPickingViewModel.update (cyclomatic 17): BattlegroundsQuestPickingViewModel.update has cyclomatic complexity 17 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/QuestPicking/BattlegroundsQuestPickingViewModel.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"a06ed5fd62a2d18582d21dbc45600985118497c285cf82af55fa09d00b21e89f"}},{"ruleId":"D1","level":"warning","message":{"text":"BobsBuddyInvoker.validateSimulationResultInternal (cyclomatic 16): BobsBuddyInvoker.validateSimulationResultInternal has cyclomatic complexity 16 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":640}}}],"partialFingerprints":{"codehealthFindingId/v1":"e7e1c9814b7c33e3a0153d01298c5440006df5b7543bf22803343b227db809d0"}},{"ruleId":"D1","level":"warning","message":{"text":"DeckSerializer.deserializeDeckString (cyclomatic 16): DeckSerializer.deserializeDeckString has cyclomatic complexity 16 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Importers/DeckSerializer.swift"},"region":{"startLine":81}}}],"partialFingerprints":{"codehealthFindingId/v1":"c952508eb485c5de96acdbb3d7cb639b40eaca98ba5340c21d2c5248c2420b26"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.getBattlegroundsHeroPickStats (cyclomatic 16): Game.getBattlegroundsHeroPickStats has cyclomatic complexity 16 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3161}}}],"partialFingerprints":{"codehealthFindingId/v1":"f6608554f13bfddf301bec5af0860f40f4eaa526cedaa43c2139d832d57827d2"}},{"ruleId":"D1","level":"warning","message":{"text":"Game.handlePlayerEntitiesChosen (cyclomatic 16): Game.handlePlayerEntitiesChosen has cyclomatic complexity 16 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4490}}}],"partialFingerprints":{"codehealthFindingId/v1":"9fb0600c6b24567064a248af926239820c786c45b87c78d86b2f38d80085e62e"}},{"ruleId":"D1","level":"warning","message":{"text":"RelatedCardsManager.buildBars (cyclomatic 16): RelatedCardsManager.buildBars has cyclomatic complexity 16 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":409}}}],"partialFingerprints":{"codehealthFindingId/v1":"152beb7441735cbff0752b7f0fae6fb523ce938b3caee30fc2689fe70fe324a5"}},{"ruleId":"D1","level":"warning","message":{"text":"AnachronosTurnCounter.handleTagChange (cyclomatic 16): AnachronosTurnCounter.handleTagChange has cyclomatic complexity 16 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AnachronosTurnCounter.swift"},"region":{"startLine":77}}}],"partialFingerprints":{"codehealthFindingId/v1":"b6989b696a43e1dc4789f81c90ba7f7fed96039992e1d4b1286048060b830350"}},{"ruleId":"D1","level":"warning","message":{"text":"SelfDamageCounter.handleTagChange (cyclomatic 16): SelfDamageCounter.handleTagChange has cyclomatic complexity 16 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/SelfDamageCounter.swift"},"region":{"startLine":54}}}],"partialFingerprints":{"codehealthFindingId/v1":"60fdd558a2562679372ee8b6a1e5ca720368aaa5e237c27c379e73457cc0544c"}},{"ruleId":"D1","level":"warning","message":{"text":"Tier7PreLobbyViewModel.update (cyclomatic 16): Tier7PreLobbyViewModel.update has cyclomatic complexity 16 (threshold 15). To reduce it, split the body: these branches sit side by side rather than nested inside one another, so extracting each one on its own would leave a function per branch. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":182}}}],"partialFingerprints":{"codehealthFindingId/v1":"53dc1f43fa42bc492ef5c8e74c73ec192931082e90683bdb0520504a431267a2"}},{"ruleId":"D1","level":"warning","message":{"text":"RootOverlayWindow.updateHoverTooltip (cyclomatic 16): RootOverlayWindow.updateHoverTooltip has cyclomatic complexity 16 (threshold 15). To reduce it, separate the cases: extract each independent branch into its own named function, and where the body has guards that only reject input, fold those into early returns at the top."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayWindow.swift"},"region":{"startLine":441}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b507e334e601595f8d3ee9efa3f02a210cdd1faf63230399d984b710ae0b6bf"}},{"ruleId":"D2","level":"warning","message":{"text":"PowerGameStateParser.handle (cognitive 1198): PowerGameStateParser.handle has cognitive complexity 1198 (threshold 15). Drivers by points: if/else 250 (860 pts), boolean chains 168, ternaries 20 (104 pts), loops 9 (56 pts), match/switch 2 (10 pts) (nesting depth added 749). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"02a8d19098f12ca8b200d0b1fea2b49d0d1281912d0aea3289278a8dfa38cd5b"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.setupInputPlayer (cognitive 113): BobsBuddyInvoker.setupInputPlayer has cognitive complexity 113 (threshold 15). Drivers by points: if/else 42 (70 pts), boolean chains 20, loops 11 (15 pts), ternaries 6 (8 pts) (nesting depth added 34). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1171}}}],"partialFingerprints":{"codehealthFindingId/v1":"1ba28faff64d91b79f9adcfc2885f568bf1413e4ce2c8b5119e580286dc0981e"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.handleHearthstoneMulliganPhase (cognitive 110): Game.handleHearthstoneMulliganPhase has cognitive complexity 110 (threshold 15). Drivers by points: if/else 20 (93 pts), boolean chains 7, ternaries 1 (7 pts), error handling 1 (2 pts), loops 1 (nesting depth added 80). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4580}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1701aca3992aefd33bc6c15b31abf484b75e56983e2fe38b4dd081d57feb9af"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.tooltipDisplay (cognitive 84): Game.tooltipDisplay has cognitive complexity 84 (threshold 15). Drivers by points: ternaries 12 (46 pts), if/else 12 (23 pts), boolean chains 15 (nesting depth added 45). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5105}}}],"partialFingerprints":{"codehealthFindingId/v1":"6738f6e581caff0b16071a918ac574214b497da7c9ecfe58f1ab2e81d5d94aae"}},{"ruleId":"D2","level":"warning","message":{"text":"ArenaVectorPath.path (cognitive 72): ArenaVectorPath.path has cognitive complexity 72 (threshold 15). Drivers by points: if/else 17 (34 pts), boolean chains 19, ternaries 4 (10 pts), loops 5 (7 pts), match/switch 1 (2 pts) (nesting depth added 26). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaVectorIcons.swift"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"d71c38c6491b1ab4dfcf9c1ffd583267a8865bfffadcda46cabdf56c9e45f493"}},{"ruleId":"D2","level":"warning","message":{"text":"CoreManager.setup (cognitive 71): CoreManager.setup has cognitive complexity 71 (threshold 15). Drivers by points: if/else 15 (37 pts), loops 6 (19 pts), match/switch 1 (7 pts), error handling 3 (6 pts), boolean chains 2 (nesting depth added 44). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":136}}}],"partialFingerprints":{"codehealthFindingId/v1":"eac82662496a2ed51ae9532e1ca6bb857d9e6801f666d8306235c98c60e11d04"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.generateEndgameStatistics (cognitive 71): Game.generateEndgameStatistics has cognitive complexity 71 (threshold 15). Drivers by points: if/else 20 (31 pts), ternaries 12 (30 pts), boolean chains 7, loops 1 (3 pts) (nesting depth added 31). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2158}}}],"partialFingerprints":{"codehealthFindingId/v1":"56873e58d4beb23c60b5d43eb4a8ea29ed7a062db03229929269bd66a7926890"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsDb.update (cognitive 71): BattlegroundsDb.update has cognitive complexity 71 (threshold 15). Drivers by points: if/else 23 (53 pts), loops 7 (14 pts), boolean chains 4 (nesting depth added 37). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BattlegroundsDb.swift"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"eaec2211076d0c9f2a22b84a5375412438ea17530720737bcd4957d388845f81"}},{"ruleId":"D2","level":"warning","message":{"text":"LoadingScreenHandler.handle (cognitive 71): LoadingScreenHandler.handle has cognitive complexity 71 (threshold 15). Drivers by points: if/else 27 (52 pts), boolean chains 19 (nesting depth added 25). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/LoadingScreenHandler.swift"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"3a3163bc6fe77e3d9ca9f2b3ce7b0376a12add0a2b877f1beddfed3b7eb92c3d"}},{"ruleId":"D2","level":"warning","message":{"text":"DungeonRunDeckWatcher.update (cognitive 64): DungeonRunDeckWatcher.update has cognitive complexity 64 (threshold 15). Drivers by points: if/else 24 (50 pts), boolean chains 14 (nesting depth added 26). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/DungeonRunWatcher.swift"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"108e7e55084fc78687b98809ab093707d345eebe4c6c4d88cca997b2078ea6c7"}},{"ruleId":"D2","level":"warning","message":{"text":"CardBar.draw (cognitive 64): CardBar.draw has cognitive complexity 64 (threshold 15). Drivers by points: if/else 20 (32 pts), boolean chains 22, ternaries 4 (10 pts) (nesting depth added 18). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":297}}}],"partialFingerprints":{"codehealthFindingId/v1":"66192585d853adf1575c3ff046070fe4e1891b36ecba5bd02ffdf8587b4431ac"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.runSimulation (cognitive 60): BobsBuddyInvoker.runSimulation has cognitive complexity 60 (threshold 15). Drivers by points: if/else 14 (35 pts), boolean chains 9, loops 3 (8 pts), error handling 3 (6 pts), ternaries 1 (2 pts) (nesting depth added 30). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":369}}}],"partialFingerprints":{"codehealthFindingId/v1":"94c126c202cad672a3b4973879d14b1e81c367c07717e7b1e338c4863f1e84f2"}},{"ruleId":"D2","level":"warning","message":{"text":"TrackerPanelLayout.init (cognitive 59): TrackerPanelLayout.init has cognitive complexity 59 (threshold 15). Drivers by points: if/else 14 (32 pts), ternaries 4 (13 pts), boolean chains 10, match/switch 2 (3 pts), loops 1 (nesting depth added 28). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Trackers/TrackerPanelView.swift"},"region":{"startLine":336}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd19714c0cc37db93a59f120256fe6702719032e4040ea4794469e7beb44f01a"}},{"ruleId":"D2","level":"warning","message":{"text":"UploadMetaData.getPlayerInfo (cognitive 57): UploadMetaData.getPlayerInfo has cognitive complexity 57 (threshold 15). Drivers by points: if/else 30 (51 pts), boolean chains 6 (nesting depth added 21). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/Data/UploadMetaData.swift"},"region":{"startLine":179}}}],"partialFingerprints":{"codehealthFindingId/v1":"bc06e5762e9e5e72023aa967ec484c7e0e96989fe857e73d388814f28be14f3a"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromDeck (cognitive 54): TagChangeActions.zoneChangeFromDeck has cognitive complexity 54 (threshold 15). Drivers by points: if/else 23 (45 pts), boolean chains 8, match/switch 1 (nesting depth added 22). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1411}}}],"partialFingerprints":{"codehealthFindingId/v1":"780496f27cbfa41b9bdcd4bd1e430487cab2b773c46613501fbd23dfae5a4aaa"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromOther (cognitive 53): TagChangeActions.zoneChangeFromOther has cognitive complexity 53 (threshold 15). Drivers by points: if/else 22 (46 pts), boolean chains 6, match/switch 1 (nesting depth added 24). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1146}}}],"partialFingerprints":{"codehealthFindingId/v1":"f6c9b72ced664f724d4010b3b47a964938e0a410dbb51016b72c8c93434d2374"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromHand (cognitive 53): TagChangeActions.zoneChangeFromHand has cognitive complexity 53 (threshold 15). Drivers by points: if/else 23 (48 pts), boolean chains 4, match/switch 1 (nesting depth added 25). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1321}}}],"partialFingerprints":{"codehealthFindingId/v1":"d06ab43082e5183078f473f8e3439c94474483a44df22768dcb142cc5a867bdc"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.syncStats (cognitive 51): Game.syncStats has cognitive complexity 51 (threshold 15). Drivers by points: boolean chains 29, if/else 11 (22 pts) (nesting depth added 11). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2458}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1f3e28cde82265bddacc62e2691ec8e75894cf3497a0e3ee5633937b5c2b238"}},{"ruleId":"D2","level":"warning","message":{"text":"CoreManager.updateDungeonRunDeck (cognitive 50): CoreManager.updateDungeonRunDeck has cognitive complexity 50 (threshold 15). Drivers by points: if/else 19 (23 pts), boolean chains 17, ternaries 5 (7 pts), loops 1 (3 pts) (nesting depth added 8). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":664}}}],"partialFingerprints":{"codehealthFindingId/v1":"af38c8798c81b3ae6d6db422c1e27d6522886ce6a867835fcf7e7edf48087d88"}},{"ruleId":"D2","level":"warning","message":{"text":"Player.getDeckState (cognitive 49): Player.getDeckState has cognitive complexity 49 (threshold 15). Drivers by points: if/else 17 (25 pts), boolean chains 19, loops 2 (5 pts) (nesting depth added 11). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":627}}}],"partialFingerprints":{"codehealthFindingId/v1":"9fa6672036864421eb0f0858b8e60cba103ea2feaf99cfd86a92abe6abe40b81"}},{"ruleId":"D2","level":"warning","message":{"text":"LogReader.readFile (cognitive 48): LogReader.readFile has cognitive complexity 48 (threshold 15). Drivers by points: if/else 13 (33 pts), boolean chains 7, loops 2 (5 pts), error handling 1 (3 pts) (nesting depth added 25). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/LogReader.swift"},"region":{"startLine":92}}}],"partialFingerprints":{"codehealthFindingId/v1":"1f0c6d6992eda4ed15025bcd431db6228bf0dc66626a84dce30490e45863f165"}},{"ruleId":"D2","level":"warning","message":{"text":"ChoicesHandler.handle (cognitive 48): ChoicesHandler.handle has cognitive complexity 48 (threshold 15). Drivers by points: if/else 19 (38 pts), ternaries 2 (6 pts), loops 1 (3 pts), boolean chains 1 (nesting depth added 25). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/ChoicesHandler.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"8a466e03483da87f4c02ae3551ba7763e27fe19179a65fb259a340633d666a1b"}},{"ruleId":"D2","level":"warning","message":{"text":"RealmHelper.checkAndUpdateDeck (cognitive 47): RealmHelper.checkAndUpdateDeck has cognitive complexity 47 (threshold 15). Drivers by points: if/else 14 (29 pts), loops 3 (13 pts), boolean chains 3, error handling 1 (2 pts) (nesting depth added 26). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/RealmHelper.swift"},"region":{"startLine":180}}}],"partialFingerprints":{"codehealthFindingId/v1":"ae5ce0e59646889b18db173443e9162312fe6df4a8cc4e8e3dac5d8689df88d3"}},{"ruleId":"D2","level":"warning","message":{"text":"ArenaWatcher.update (cognitive 47): ArenaWatcher.update has cognitive complexity 47 (threshold 15). Drivers by points: if/else 16 (29 pts), boolean chains 14, ternaries 1 (4 pts) (nesting depth added 16). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/ArenaWatcher.swift"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"deee39494c5e20bf77b8ef5ceb23b47eb942339910dcad50a619b124de2587f0"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.handlePlayerMulliganDone (cognitive 47): Game.handlePlayerMulliganDone has cognitive complexity 47 (threshold 15). Drivers by points: if/else 11 (31 pts), error handling 2 (7 pts), boolean chains 6, loops 1 (3 pts) (nesting depth added 27). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4021}}}],"partialFingerprints":{"codehealthFindingId/v1":"4e0e088345e9043aa000adb1738a0f269e974e5fdce3d100e9e34e5204c36f93"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.handleAttack (cognitive 45): SecretsManager.handleAttack has cognitive complexity 45 (threshold 15). Drivers by points: if/else 18 (39 pts), boolean chains 6 (nesting depth added 21). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":458}}}],"partialFingerprints":{"codehealthFindingId/v1":"3f260f94f937bc9cba077aa40cbe58d218813db22c6fc21935e24c87b4d08942"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsPreferences.checkboxClicked (cognitive 45): BattlegroundsPreferences.checkboxClicked has cognitive complexity 45 (threshold 15). Drivers by points: if/else 39 (44 pts), boolean chains 1 (nesting depth added 5). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/BattlegroundsPreferences.swift"},"region":{"startLine":106}}}],"partialFingerprints":{"codehealthFindingId/v1":"abc4833fa675d8333728617018dae92c3e6285e9ffec900094fb1fd7dfde9861"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.onCardCopy (cognitive 44): TagChangeActions.onCardCopy has cognitive complexity 44 (threshold 15). Drivers by points: boolean chains 30, if/else 12 (14 pts) (nesting depth added 2). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":281}}}],"partialFingerprints":{"codehealthFindingId/v1":"0bb22b8f8aab61669448df3c2686faef62362e408fb8f1e55700671c92787a7b"}},{"ruleId":"D2","level":"warning","message":{"text":"BoardMouseOverDetection.update (cognitive 44): BoardMouseOverDetection.update has cognitive complexity 44 (threshold 15). Drivers by points: if/else 11 (26 pts), ternaries 3 (10 pts), boolean chains 4, loops 3 (4 pts) (nesting depth added 23). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Board/BoardMouseOverDetection.swift"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"f14a2b8b1f5f3096ef73ed5d79644de56dd273769c0738c88853e8ad68308669"}},{"ruleId":"D2","level":"warning","message":{"text":"OpponentHandMarkersViewModel.update (cognitive 43): OpponentHandMarkersViewModel.update has cognitive complexity 43 (threshold 15). Drivers by points: if/else 16 (35 pts), boolean chains 7, loops 1 (nesting depth added 19). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Hand/CardMarkerViewModel.swift"},"region":{"startLine":137}}}],"partialFingerprints":{"codehealthFindingId/v1":"11a4bc19a5387c504ae7ce885984e5df67adbf9d0c7ab3e133b7134e516e905a"}},{"ruleId":"D2","level":"warning","message":{"text":"Player.play (cognitive 42): Player.play has cognitive complexity 42 (threshold 15). Drivers by points: if/else 13 (29 pts), boolean chains 6, loops 1 (5 pts), match/switch 1 (2 pts) (nesting depth added 21). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":893}}}],"partialFingerprints":{"codehealthFindingId/v1":"c9dbd63b0f7544461626513504d9ada6cb749c5dcb6a8cdcd6a4e12da61d5c16"}},{"ruleId":"D2","level":"warning","message":{"text":"RootOverlayViewModel.setCardOpacityMask (cognitive 41): RootOverlayViewModel.setCardOpacityMask has cognitive complexity 41 (threshold 15). Drivers by points: if/else 11 (13 pts), boolean chains 12, loops 6 (12 pts), ternaries 2 (4 pts) (nesting depth added 10). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift"},"region":{"startLine":137}}}],"partialFingerprints":{"codehealthFindingId/v1":"ec19673b40c67f3ea1259bef6382ce2b1c26e73162cba363fb2f16e310691e0d"}},{"ruleId":"D2","level":"warning","message":{"text":"Player.opponentCardList (cognitive 39): Player.opponentCardList has cognitive complexity 39 (threshold 15). Drivers by points: boolean chains 30, if/else 5 (6 pts), ternaries 2 (3 pts) (nesting depth added 2). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":560}}}],"partialFingerprints":{"codehealthFindingId/v1":"f51d5634e54a9ceaab25ca0be0e7dd133ffbbc2dd6fc4139c206229478dcdf2b"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.zoneChange (cognitive 38): TagChangeActions.zoneChange has cognitive complexity 38 (threshold 15). Drivers by points: if/else 16 (25 pts), boolean chains 12, match/switch 1 (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1015}}}],"partialFingerprints":{"codehealthFindingId/v1":"a44b5aeae16b3f7b9aca4f40505515adda58a67df8e9822ae9f389ae83d9a718"}},{"ruleId":"D2","level":"warning","message":{"text":"TrackersPreferences.checkboxClicked (cognitive 38): TrackersPreferences.checkboxClicked has cognitive complexity 38 (threshold 15). Drivers by points: if/else 28 (37 pts), boolean chains 1 (nesting depth added 9). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/TrackersPreferences.swift"},"region":{"startLine":109}}}],"partialFingerprints":{"codehealthFindingId/v1":"b3457b99879d3a97f264939fca573cc9fcb70ceee61381372842121bfa61dd32"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.handlePlayerEntitiesChosen (cognitive 37): Game.handlePlayerEntitiesChosen has cognitive complexity 37 (threshold 15). Drivers by points: if/else 12 (33 pts), boolean chains 3, match/switch 1 (nesting depth added 21). The drivers above price the dispatch low by construction \u2014 a dispatch is charged once however many cases it lists, while each branch inside an arm is charged in full \u2014 so most of this count is what the case bodies hold, and the arms are where it can be reduced. To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4490}}}],"partialFingerprints":{"codehealthFindingId/v1":"965e085eac6076b2585f29c9ef3fb878cabde12969398633449be81f0a8efef7"}},{"ruleId":"D2","level":"warning","message":{"text":"FileImporter.fileImport (cognitive 36): FileImporter.fileImport has cognitive complexity 36 (threshold 15). Drivers by points: if/else 11 (33 pts), boolean chains 1, error handling 1, loops 1 (nesting depth added 22). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Importers/FileImporter.swift"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"42b9309d75e69f1ff3c25f02f56589f4f6f4c31eab3f1af51a8efbdb17007941"}},{"ruleId":"D2","level":"warning","message":{"text":"CoreManager.dungeonRunMatchStarted (cognitive 36): CoreManager.dungeonRunMatchStarted has cognitive complexity 36 (threshold 15). Drivers by points: if/else 14 (23 pts), boolean chains 12, ternaries 1 (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":581}}}],"partialFingerprints":{"codehealthFindingId/v1":"2cadd428e1e3ea7c707de9458c4bbe97e380f454cc9cac535d968b3ce0adcba1"}},{"ruleId":"D2","level":"warning","message":{"text":"CoreManager.autoDetectDeckWithMirror (cognitive 36): CoreManager.autoDetectDeckWithMirror has cognitive complexity 36 (threshold 15). Drivers by points: if/else 13 (33 pts), boolean chains 3 (nesting depth added 20). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":781}}}],"partialFingerprints":{"codehealthFindingId/v1":"58f070daa336e6338e592549a744cb851b430623db33489090ae6ef89a4d4eca"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.updateOpponentTracker (cognitive 36): Game.updateOpponentTracker has cognitive complexity 36 (threshold 15). Drivers by points: boolean chains 20, if/else 8 (12 pts), ternaries 2 (4 pts) (nesting depth added 6). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":287}}}],"partialFingerprints":{"codehealthFindingId/v1":"5e1fab05a8c5d0f5a6c4ea3d1385c3c3565b5dc362a53452d034b4d244952b22"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.handleCardPlayed (cognitive 36): SecretsManager.handleCardPlayed has cognitive complexity 36 (threshold 15). Drivers by points: if/else 18 (31 pts), boolean chains 5 (nesting depth added 13). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":791}}}],"partialFingerprints":{"codehealthFindingId/v1":"393a3acd1c7580ec1488c7d2fed033bc3d442af75571f3c6682d89a5846df12a"}},{"ruleId":"D2","level":"warning","message":{"text":"AppDelegate.applicationDidFinishLaunching (cognitive 35): AppDelegate.applicationDidFinishLaunching has cognitive complexity 35 (threshold 15). Drivers by points: if/else 18 (24 pts), match/switch 3 (7 pts), ternaries 2 (4 pts) (nesting depth added 12). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":82}}}],"partialFingerprints":{"codehealthFindingId/v1":"1a6d18bac791e12d8d8f16c791482f0c436334f36131ffdf7568f97039a8236b"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.getMinionFromEntity (cognitive 35): BobsBuddyInvoker.getMinionFromEntity has cognitive complexity 35 (threshold 15). Drivers by points: if/else 11 (22 pts), boolean chains 8, match/switch 1 (2 pts), ternaries 1 (2 pts), loops 1 (nesting depth added 13). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":781}}}],"partialFingerprints":{"codehealthFindingId/v1":"32d37f725f16461bee5960b8e587fa52d2a13bf7da0938f2384b258ae5a8c6b5"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsPreferences.viewWillAppear (cognitive 35): BattlegroundsPreferences.viewWillAppear has cognitive complexity 35 (threshold 15). Drivers by points: ternaries 33, boolean chains 1, if/else 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/BattlegroundsPreferences.swift"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"fe791f92a3b4361c6428e5f5ac412d183464fadd629e33292ffc769336c73db1"}},{"ruleId":"D2","level":"warning","message":{"text":"HearthstoneWindow.reload (cognitive 34): HearthstoneWindow.reload has cognitive complexity 34 (threshold 15). Drivers by points: if/else 11 (24 pts), boolean chains 6, loops 1 (4 pts) (nesting depth added 16). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/SizeHelper.swift"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"d435d4a39e9b6f4cb8dcce77d734b0856e42d8b64edee051c479d016b98401e2"}},{"ruleId":"D2","level":"warning","message":{"text":"AnimatedCardList.update (cognitive 34): AnimatedCardList.update has cognitive complexity 34 (threshold 15). Drivers by points: if/else 14 (26 pts), loops 5, boolean chains 3 (nesting depth added 12). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Trackers/AnimatedCardList.swift"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"2acbc1a9900e9c813f7cfc76be2c8e2f17dabede638a6f0a5ffb4bebd5a3a805"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.updatePlayerTracker (cognitive 33): Game.updatePlayerTracker has cognitive complexity 33 (threshold 15). Drivers by points: boolean chains 15, if/else 9 (14 pts), ternaries 2 (4 pts) (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":382}}}],"partialFingerprints":{"codehealthFindingId/v1":"a2d629160b73b6db48d0e3b6564aea6895c9bc2f4f9cbfe5bdd4696f0480055d"}},{"ruleId":"D2","level":"warning","message":{"text":"CurveView.draw (cognitive 33): CurveView.draw has cognitive complexity 33 (threshold 15). Drivers by points: if/else 12 (23 pts), loops 2 (4 pts), match/switch 1 (4 pts), boolean chains 2 (nesting depth added 16). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/CurveView.swift"},"region":{"startLine":52}}}],"partialFingerprints":{"codehealthFindingId/v1":"e94b7f66822b2ab5547b58d93ec8d8a5888c9caf1ce9e5f812b3d2d50ca6edb1"}},{"ruleId":"D2","level":"warning","message":{"text":"LogReaderManager.startLogReaders (cognitive 32): LogReaderManager.startLogReaders has cognitive complexity 32 (threshold 15). Drivers by points: if/else 5 (19 pts), loops 6 (13 pts) (nesting depth added 21). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/LogReaderManager.swift"},"region":{"startLine":120}}}],"partialFingerprints":{"codehealthFindingId/v1":"4231324c88e7669dad861737cba29d93f54b58ae1a9153a70126bb42ffc934b5"}},{"ruleId":"D2","level":"warning","message":{"text":"CardTooltipPanel.show (cognitive 32): CardTooltipPanel.show has cognitive complexity 32 (threshold 15). Drivers by points: if/else 14 (20 pts), ternaries 5 (7 pts), boolean chains 4, match/switch 1 (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift"},"region":{"startLine":305}}}],"partialFingerprints":{"codehealthFindingId/v1":"2830a15732e02d036354e435b62dbc2195cd8d3cfcf5a226cc86f9a613e7adc3"}},{"ruleId":"D2","level":"warning","message":{"text":"AppDelegate.buildMenu (cognitive 31): AppDelegate.buildMenu has cognitive complexity 31 (threshold 15). Drivers by points: if/else 12 (21 pts), ternaries 2 (4 pts), boolean chains 3, loops 2 (3 pts) (nesting depth added 12). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":589}}}],"partialFingerprints":{"codehealthFindingId/v1":"8a3563c66e4cd8d0d620b3e3e72347ec238d0658abbf4821e676432bcdf19be2"}},{"ruleId":"D2","level":"warning","message":{"text":"ArenaStateWatcher.updateCore (cognitive 31): ArenaStateWatcher.updateCore has cognitive complexity 31 (threshold 15). Drivers by points: if/else 20 (22 pts), boolean chains 7, ternaries 1 (2 pts) (nesting depth added 3). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/Arena/ArenaStateWatcher.swift"},"region":{"startLine":196}}}],"partialFingerprints":{"codehealthFindingId/v1":"967fb88ee940a1b9b86ca157e73f4d925c1e4e0372043469a90953f4036e9b2a"}},{"ruleId":"D2","level":"warning","message":{"text":"MonoHelper.testSimulation (cognitive 30): MonoHelper.testSimulation has cognitive complexity 30 (threshold 15). Drivers by points: if/else 8 (19 pts), loops 2 (6 pts), ternaries 1 (4 pts), boolean chains 1 (nesting depth added 18). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":516}}}],"partialFingerprints":{"codehealthFindingId/v1":"2e65637c6ddf7882b571df054a37812c3257e0f29e690cfd75e0f1c10186d2c1"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.getSecretsCreatedBy (cognitive 30): SecretsManager.getSecretsCreatedBy has cognitive complexity 30 (threshold 15). Drivers by points: if/else 14 (23 pts), boolean chains 6, loops 1 (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"5e69c1a68806f3544ed32dbd22f6304cd5b84095a4eee8eb60259816a0008902"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.checkForDarkGiftsOnMagnetizedModules (cognitive 29): BobsBuddyInvoker.checkForDarkGiftsOnMagnetizedModules has cognitive complexity 29 (threshold 15). Drivers by points: if/else 7 (22 pts), loops 2 (3 pts), match/switch 1 (3 pts), boolean chains 1 (nesting depth added 18). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1051}}}],"partialFingerprints":{"codehealthFindingId/v1":"245b27bc81bda218bc54658ac6cc03aeca302e223dcbbbcde129f3c554fc4758"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.setRelatedCardsTrigger (cognitive 29): Game.setRelatedCardsTrigger has cognitive complexity 29 (threshold 15). Drivers by points: if/else 9 (13 pts), ternaries 5 (8 pts), boolean chains 5, match/switch 3 (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5287}}}],"partialFingerprints":{"codehealthFindingId/v1":"28df0075f03f0bf044120b5b8830e6135c765351b301091b9b2ea4fa17ed0c7f"}},{"ruleId":"D2","level":"warning","message":{"text":"UploadMetaData.generate (cognitive 28): UploadMetaData.generate has cognitive complexity 28 (threshold 15). Drivers by points: if/else 19 (26 pts), boolean chains 1, ternaries 1 (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/Data/UploadMetaData.swift"},"region":{"startLine":85}}}],"partialFingerprints":{"codehealthFindingId/v1":"c0e511705fb03c6c0818eb5068337b9b5cb92ae397cb307b319ab596c1a72347"}},{"ruleId":"D2","level":"warning","message":{"text":"RelatedCardsManager.tryGetBucketedRelatedCardsSummary (cognitive 28): RelatedCardsManager.tryGetBucketedRelatedCardsSummary has cognitive complexity 28 (threshold 15). Drivers by points: if/else 7 (13 pts), loops 5 (10 pts), ternaries 4, boolean chains 1 (nesting depth added 11). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":290}}}],"partialFingerprints":{"codehealthFindingId/v1":"726d86fbf3bc5d3184b6be2aa2ce49fe2c41cd2b0a2bddb5757ce8bb93ff69a7"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.creatorChanged (cognitive 27): TagChangeActions.creatorChanged has cognitive complexity 27 (threshold 15). Drivers by points: if/else 10 (21 pts), boolean chains 6 (nesting depth added 11). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":507}}}],"partialFingerprints":{"codehealthFindingId/v1":"2ade4050fafd6106d5d25fe7dc4869a853ad2652a025dfdceca7b18754dcaa12"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsMinionPinningViewModel.recomputeRecommendedFromGuides (cognitive 27): BattlegroundsMinionPinningViewModel.recomputeRecommendedFromGuides has cognitive complexity 27 (threshold 15). Drivers by points: if/else 7 (19 pts), loops 3 (6 pts), boolean chains 2 (nesting depth added 15). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningViewModel.swift"},"region":{"startLine":324}}}],"partialFingerprints":{"codehealthFindingId/v1":"8f2ac14fbf4340f3a685afb68b34a0278c8b5508dd4718bcb16f3e0aad673711"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.checkForRepeatedMagnetizedAutoAssemblers (cognitive 26): BobsBuddyInvoker.checkForRepeatedMagnetizedAutoAssemblers has cognitive complexity 26 (threshold 15). Drivers by points: if/else 10 (17 pts), boolean chains 4, loops 3, ternaries 1 (2 pts) (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":963}}}],"partialFingerprints":{"codehealthFindingId/v1":"9bfd7b0f8da5a78584f1ac1b9f1ee81e0703da87785a3fb14abc9325e82012bb"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.snapshotBoardState (cognitive 26): BobsBuddyInvoker.snapshotBoardState has cognitive complexity 26 (threshold 15). Drivers by points: if/else 10 (19 pts), boolean chains 6, error handling 1 (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1519}}}],"partialFingerprints":{"codehealthFindingId/v1":"ba9a4efcc57f16a1f65047fd481463ae19c3e86fe167f0cb7e82697fd273a58d"}},{"ruleId":"D2","level":"warning","message":{"text":"Player.getOpponentDeckState (cognitive 26): Player.getOpponentDeckState has cognitive complexity 26 (threshold 15). Drivers by points: boolean chains 14, if/else 10 (12 pts) (nesting depth added 2). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":761}}}],"partialFingerprints":{"codehealthFindingId/v1":"fd8e9ea1a80891354389355a6798281b9e9ffb4e365ab9bd51efaf2c61ca1ee9"}},{"ruleId":"D2","level":"warning","message":{"text":"StatsHelper.getLadderTableData (cognitive 26): StatsHelper.getLadderTableData has cognitive complexity 26 (threshold 15). Drivers by points: if/else 13 (23 pts), boolean chains 2, loops 1 (nesting depth added 10). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Statistics/StatsHelper.swift"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"1e9d103044a5c9a74118486ae55cf79972579188f3dfb765518be3afa0319bdc"}},{"ruleId":"D2","level":"warning","message":{"text":"WindowManager.show (cognitive 26): WindowManager.show has cognitive complexity 26 (threshold 15). Drivers by points: if/else 14 (22 pts), boolean chains 4 (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Trackers/WindowManager.swift"},"region":{"startLine":63}}}],"partialFingerprints":{"codehealthFindingId/v1":"b7c78b70b37a946865dd051ac468086326c747a1d9d21e01f219c2a396d330ea"}},{"ruleId":"D2","level":"warning","message":{"text":"AnimalCompanionCounter.handleTagChange (cognitive 26): AnimalCompanionCounter.handleTagChange has cognitive complexity 26 (threshold 15). Drivers by points: if/else 11 (15 pts), boolean chains 6, match/switch 2 (5 pts) (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AnimalCompanionCounter.swift"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"43d0eb6cf21a810134cbfc11116c0354094a6c0b33cf7786af3187123014849c"}},{"ruleId":"D2","level":"warning","message":{"text":"LogUploader.upload (cognitive 25): LogUploader.upload has cognitive complexity 25 (threshold 15). Drivers by points: if/else 12 (14 pts), boolean chains 11 (nesting depth added 2). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/LogUploader.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"7af41e5670002d256f34864c9822d7eb8f5f4e82d5d026ab0af0cffb3eda7afa"}},{"ruleId":"D2","level":"warning","message":{"text":"DungeonRunDeckWatcher.updateDungeonInfo (cognitive 25): DungeonRunDeckWatcher.updateDungeonInfo has cognitive complexity 25 (threshold 15). Drivers by points: if/else 8 (15 pts), boolean chains 7, match/switch 1 (3 pts) (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/DungeonRunWatcher.swift"},"region":{"startLine":150}}}],"partialFingerprints":{"codehealthFindingId/v1":"f81148be5a4a849692374ef1afa68d157841ee0ef2331c3808000b728dc58da3"}},{"ruleId":"D2","level":"warning","message":{"text":"CardUtils.isAllowedInGenerationPool (cognitive 25): CardUtils.isAllowedInGenerationPool has cognitive complexity 25 (threshold 15). Drivers by points: boolean chains 14, if/else 11. To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CardUtils.swift"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"e1df34a01b72b94284f918090b8dad1b9866b8d167dfbba4b649b6c80530efc7"}},{"ruleId":"D2","level":"warning","message":{"text":"Card.init (cognitive 25): Card.init has cognitive complexity 25 (threshold 15). Drivers by points: if/else 6 (17 pts), match/switch 1 (4 pts), loops 1 (3 pts), error handling 1 (nesting depth added 16). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Models/Card.swift"},"region":{"startLine":389}}}],"partialFingerprints":{"codehealthFindingId/v1":"d9ebceb16ebf6518905493393ce54b99e0c6ce91541060c55996d838f988c34e"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.getArenaCreatedSecrets (cognitive 25): SecretsManager.getArenaCreatedSecrets has cognitive complexity 25 (threshold 15). Drivers by points: if/else 11 (20 pts), boolean chains 3, loops 1 (2 pts) (nesting depth added 10). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":280}}}],"partialFingerprints":{"codehealthFindingId/v1":"2dcfec231c694a6a9a7f4fa3edb181bd01feada54fb2f7a979423d305a9387b1"}},{"ruleId":"D2","level":"warning","message":{"text":"ConstructedMulliganGuidePreLobbyViewModel.update (cognitive 25): ConstructedMulliganGuidePreLobbyViewModel.update has cognitive complexity 25 (threshold 15). Drivers by points: if/else 9 (14 pts), loops 3 (5 pts), boolean chains 3, match/switch 1 (3 pts) (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift"},"region":{"startLine":430}}}],"partialFingerprints":{"codehealthFindingId/v1":"d1dd1eb1f9a9ec5a2c955039090a9253f4555c0dabdca9b6e1ac28b168c12cbd"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.handleEndGame (cognitive 24): Game.handleEndGame has cognitive complexity 24 (threshold 15). Drivers by points: if/else 12 (18 pts), boolean chains 6 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2322}}}],"partialFingerprints":{"codehealthFindingId/v1":"af03719840089c6649e183d65c013431eec130d63fce80ac75fec55b7d5138c2"}},{"ruleId":"D2","level":"warning","message":{"text":"LogReader.findInitialOffset (cognitive 24): LogReader.findInitialOffset has cognitive complexity 24 (threshold 15). Drivers by points: if/else 6 (16 pts), loops 3 (7 pts), boolean chains 1 (nesting depth added 14). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/LogReader.swift"},"region":{"startLine":217}}}],"partialFingerprints":{"codehealthFindingId/v1":"3c5f857c939933917827f0d6ac07ad94c705b874f89eb746fe2904ee18631001"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsMinionView.draw (cognitive 24): BattlegroundsMinionView.draw has cognitive complexity 24 (threshold 15). Drivers by points: if/else 14 (15 pts), boolean chains 8, ternaries 1 (nesting depth added 1). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BattlegroundsMinionView.swift"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"3fa4fa966a91082f463380736709cd5e6e6daaa00d02fa57fed19f873a5af48d"}},{"ruleId":"D2","level":"warning","message":{"text":"Database.apply (cognitive 23): Database.apply has cognitive complexity 23 (threshold 15). Drivers by points: if/else 10 (18 pts), boolean chains 4, match/switch 1 (nesting depth added 8). The drivers above price the dispatch low by construction \u2014 a dispatch is charged once however many cases it lists, while each branch inside an arm is charged in full \u2014 so most of this count is what the case bodies hold, and the arms are where it can be reduced. To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Database.swift"},"region":{"startLine":167}}}],"partialFingerprints":{"codehealthFindingId/v1":"810c1ae752a805833911358b30df7cbff8fac042a8ac8a49bddc60e4e9aa0730"}},{"ruleId":"D2","level":"warning","message":{"text":"Collection.init (cognitive 23): Collection.init has cognitive complexity 23 (threshold 15). Drivers by points: if/else 7 (18 pts), loops 3, boolean chains 2 (nesting depth added 11). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Collection.swift"},"region":{"startLine":57}}}],"partialFingerprints":{"codehealthFindingId/v1":"00772050a8b94951e03ff58b088ee8f3a47edac31acab4482e627f3e09ed22b1"}},{"ruleId":"D2","level":"warning","message":{"text":"ModuleMetadata.init (cognitive 23): ModuleMetadata.init has cognitive complexity 23 (threshold 15). Drivers by points: if/else 6 (10 pts), boolean chains 5, ternaries 2 (4 pts), loops 2, match/switch 1 (2 pts) (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/ReflectionHelper.swift"},"region":{"startLine":166}}}],"partialFingerprints":{"codehealthFindingId/v1":"b73693029e477b07731c0114d978dd8dc876e5ef122a55cc4123505ea0cf82db"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.handleTurnsInPlayChange (cognitive 23): SecretsManager.handleTurnsInPlayChange has cognitive complexity 23 (threshold 15). Drivers by points: if/else 9 (15 pts), boolean chains 6, ternaries 1 (2 pts) (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":731}}}],"partialFingerprints":{"codehealthFindingId/v1":"647828e716ce33818f1902a410194a6d92afebc5d8174cfb25f05e7edbcb64ad"}},{"ruleId":"D2","level":"warning","message":{"text":"ArenaPickHelperViewModel.onDeckEditing (cognitive 23): ArenaPickHelperViewModel.onDeckEditing has cognitive complexity 23 (threshold 15). Drivers by points: if/else 8 (13 pts), boolean chains 5, ternaries 2 (3 pts), loops 2 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPickHelperViewModel.swift"},"region":{"startLine":641}}}],"partialFingerprints":{"codehealthFindingId/v1":"a160ce6f8ee5e34393369e6c8c48dd3fb7b15ca7fb3499fc1cf23222f1ebd7fd"}},{"ruleId":"D2","level":"warning","message":{"text":"PlayerTrackersPreferences.checkboxClicked (cognitive 23): PlayerTrackersPreferences.checkboxClicked has cognitive complexity 23 (threshold 15). Drivers by points: if/else 22 (23 pts) (nesting depth added 1). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/PlayerTrackersPreferences.swift"},"region":{"startLine":77}}}],"partialFingerprints":{"codehealthFindingId/v1":"4a22d28fb63703de74db847106b83a5b0ca30af3c303a29d1874a8d97ddd1f01"}},{"ruleId":"D2","level":"warning","message":{"text":"TrackersPreferences.viewWillAppear (cognitive 23): TrackersPreferences.viewWillAppear has cognitive complexity 23 (threshold 15). Drivers by points: ternaries 21, if/else 1, match/switch 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/TrackersPreferences.swift"},"region":{"startLine":45}}}],"partialFingerprints":{"codehealthFindingId/v1":"4311f6abbe4b21874ce41e33d15fd3f98c05b69d9c1bcaeb1edb54c0d36e166c"}},{"ruleId":"D2","level":"warning","message":{"text":"CoreManager.filterDeck (cognitive 22): CoreManager.filterDeck has cognitive complexity 22 (threshold 15). Drivers by points: if/else 6 (16 pts), boolean chains 6 (nesting depth added 10). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":562}}}],"partialFingerprints":{"codehealthFindingId/v1":"77d47064a98d1614358c2cc7bf262a66771ea31d65b2b53a75e51d5960d01e87"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.updateBoardStateTrackers (cognitive 22): Game.updateBoardStateTrackers has cognitive complexity 22 (threshold 15). Drivers by points: if/else 8 (14 pts), boolean chains 8 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":813}}}],"partialFingerprints":{"codehealthFindingId/v1":"eacdb50cdde1ebfea5f107c1bcf925848ff9d1bdd1088b7503c3236b4e869c2f"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.gameStart (cognitive 22): Game.gameStart has cognitive complexity 22 (threshold 15). Drivers by points: if/else 14 (15 pts), boolean chains 7 (nesting depth added 1). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":1939}}}],"partialFingerprints":{"codehealthFindingId/v1":"83cfb33aa026b79ceac3a60ae5ea7f4e242519e2f11ae789c242128a8238fc61"}},{"ruleId":"D2","level":"warning","message":{"text":"RelatedCardsManager.tryGetRelatedCardsSummary (cognitive 22): RelatedCardsManager.tryGetRelatedCardsSummary has cognitive complexity 22 (threshold 15). Drivers by points: if/else 6 (9 pts), loops 3 (6 pts), ternaries 4 (6 pts), boolean chains 1 (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":210}}}],"partialFingerprints":{"codehealthFindingId/v1":"bed4761a37c47324b8055e5b77c2e64db54531234289a474ecb098ed1316b86b"}},{"ruleId":"D2","level":"warning","message":{"text":"RootOverlayWindow.updateHoverTooltip (cognitive 22): RootOverlayWindow.updateHoverTooltip has cognitive complexity 22 (threshold 15). Drivers by points: if/else 8 (12 pts), match/switch 2 (6 pts), boolean chains 4 (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayWindow.swift"},"region":{"startLine":441}}}],"partialFingerprints":{"codehealthFindingId/v1":"d8ea3e84066799c9f7b7db079e005afaf62882ea98f07f5306eedba8aeb3807d"}},{"ruleId":"D2","level":"warning","message":{"text":"PVPDungeonRunWatcher.updatePVPDungeonInfo (cognitive 21): PVPDungeonRunWatcher.updatePVPDungeonInfo has cognitive complexity 21 (threshold 15). Drivers by points: if/else 9 (18 pts), boolean chains 3 (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/PVPDungeonRunWatcher.swift"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"5b12aa43c766ed89a3708798f8da5beccd04e0d6aca6ff6b78cb09fe521c24a3"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.handleOpponentMinionDeath (cognitive 21): SecretsManager.handleOpponentMinionDeath has cognitive complexity 21 (threshold 15). Drivers by points: if/else 11 (14 pts), boolean chains 7 (nesting depth added 3). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":607}}}],"partialFingerprints":{"codehealthFindingId/v1":"25094bf78d2a60bf339dbd834fc6d227aaa1fdf5c19f03e3c3cbc2b27f24a661"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeHandler.parseEntity (cognitive 21): TagChangeHandler.parseEntity has cognitive complexity 21 (threshold 15). Drivers by points: if/else 14 (21 pts) (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeHandler.swift"},"region":{"startLine":108}}}],"partialFingerprints":{"codehealthFindingId/v1":"4698349056be0a60afea266a34ba0ef6ec9fc98c5a362436e1580cbad996e74a"}},{"ruleId":"D2","level":"warning","message":{"text":"OpponentTrackersPreferences.checkboxClicked (cognitive 21): OpponentTrackersPreferences.checkboxClicked has cognitive complexity 21 (threshold 15). Drivers by points: if/else 20 (21 pts) (nesting depth added 1). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/OpponentTrackersPreferences.swift"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"511714e5a160336c4acba6a8729db3f7c39c0bf9a33f453ee1a7673302b6b69c"}},{"ruleId":"D2","level":"warning","message":{"text":"PlayerTrackersPreferences.viewWillAppear (cognitive 21): PlayerTrackersPreferences.viewWillAppear has cognitive complexity 21 (threshold 15). Drivers by points: ternaries 20, if/else 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/PlayerTrackersPreferences.swift"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"820a8b9d8a517e015c4bd9d8cc09dfec623973f180261605a1274aa41532cecd"}},{"ruleId":"D2","level":"warning","message":{"text":"Cards.search (cognitive 20): Cards.search has cognitive complexity 20 (threshold 15). Drivers by points: if/else 11 (12 pts), boolean chains 8 (nesting depth added 1). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Cards.swift"},"region":{"startLine":158}}}],"partialFingerprints":{"codehealthFindingId/v1":"bc49b5ffc8d6ed37f46dd1870bf21a6c7deb6d63b60b137a803c9a9b2cdb0061"}},{"ruleId":"D2","level":"warning","message":{"text":"RelatedCardsManager.buildBars (cognitive 20): RelatedCardsManager.buildBars has cognitive complexity 20 (threshold 15). Drivers by points: ternaries 6 (9 pts), if/else 4 (6 pts), loops 4, boolean chains 1 (nesting depth added 5). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":409}}}],"partialFingerprints":{"codehealthFindingId/v1":"78cdcdd1b013fefa32bbe560cf947f2c842f6dcf28d217031e71d6cd02ee55c9"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.setHeroAsync (cognitive 20): TagChangeActions.setHeroAsync has cognitive complexity 20 (threshold 15). Drivers by points: if/else 8 (12 pts), boolean chains 4, loops 2 (4 pts) (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1502}}}],"partialFingerprints":{"codehealthFindingId/v1":"a2f9fd2a985224137916ab3c8db15cb66d562a8d532e1da003ac1b5f66ea9a41"}},{"ruleId":"D2","level":"warning","message":{"text":"CardBar.addCardImage (cognitive 20): CardBar.addCardImage has cognitive complexity 20 (threshold 15). Drivers by points: if/else 10 (12 pts), boolean chains 7, ternaries 1 (nesting depth added 2). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":379}}}],"partialFingerprints":{"codehealthFindingId/v1":"638d2a11a24bc651b42acecf259e04fa0f8e1d3a63c6138b4618af88c5800c87"}},{"ruleId":"D2","level":"warning","message":{"text":"DeckManager.tableView (cognitive 20): DeckManager.tableView has cognitive complexity 20 (threshold 15). Drivers by points: if/else 6 (9 pts), ternaries 2 (6 pts), match/switch 1 (3 pts), boolean chains 2 (nesting depth added 9). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/DeckManager.swift"},"region":{"startLine":738}}}],"partialFingerprints":{"codehealthFindingId/v1":"4106bc3791056af8509801d822c75333450d3bfafcd40c89074aeb897eb7ea13"}},{"ruleId":"D2","level":"warning","message":{"text":"StatusMessageConverter.getStatusMessage (cognitive 19): StatusMessageConverter.getStatusMessage has cognitive complexity 19 (threshold 15). Drivers by points: ternaries 6 (12 pts), if/else 3 (4 pts), match/switch 2 (3 pts) (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/StatusMessageConverter.swift"},"region":{"startLine":12}}}],"partialFingerprints":{"codehealthFindingId/v1":"36b3ac959e495f174f9540677f7c450ffe60dc826e26cc92f5a1776328016360"}},{"ruleId":"D2","level":"warning","message":{"text":"DeckSerializer.deserializeDeckString (cognitive 19): DeckSerializer.deserializeDeckString has cognitive complexity 19 (threshold 15). Drivers by points: if/else 8 (9 pts), loops 6 (9 pts), boolean chains 1 (nesting depth added 4). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Importers/DeckSerializer.swift"},"region":{"startLine":81}}}],"partialFingerprints":{"codehealthFindingId/v1":"dc99150e93d5b35927804b7ce750eb8ef1cf83bf4604114ef14f3db9a81224ef"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.zoneChangeFromPlay (cognitive 19): TagChangeActions.zoneChangeFromPlay has cognitive complexity 19 (threshold 15). Drivers by points: if/else 11 (15 pts), boolean chains 3, match/switch 1 (nesting depth added 4). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1272}}}],"partialFingerprints":{"codehealthFindingId/v1":"2c54433c02dc6cd7e83bcb91a7c5e29df1b9e9920bb833b98f461ab7427c318d"}},{"ruleId":"D2","level":"warning","message":{"text":"OpponentTrackersPreferences.viewWillAppear (cognitive 19): OpponentTrackersPreferences.viewWillAppear has cognitive complexity 19 (threshold 15). Drivers by points: ternaries 18, if/else 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/OpponentTrackersPreferences.swift"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"6a7ff6ecc45935bc76fd56e5665316ef75f67adea14c7a0f05b98060c0f4b880"}},{"ruleId":"D2","level":"warning","message":{"text":"GoldNextTurnCounter.handleTagChange (cognitive 19): GoldNextTurnCounter.handleTagChange has cognitive complexity 19 (threshold 15). Drivers by points: boolean chains 10, if/else 8 (9 pts) (nesting depth added 1). To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/GoldNextTurnCounter.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b836eadc65e26cc55c97c0206efc179db340de98804d4feaa88461c3343c7e6"}},{"ruleId":"D2","level":"warning","message":{"text":"Tier7PreLobbyViewModel.update (cognitive 19): Tier7PreLobbyViewModel.update has cognitive complexity 19 (threshold 15). Drivers by points: if/else 15 (17 pts), boolean chains 2 (nesting depth added 2). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":182}}}],"partialFingerprints":{"codehealthFindingId/v1":"2180028d3486fea8a7a05e3f3bfff06c293f36b4f5a7296c80f2de21b60025bd"}},{"ruleId":"D2","level":"warning","message":{"text":"BoardOrderViewModel.update (cognitive 19): BoardOrderViewModel.update has cognitive complexity 19 (threshold 15). Drivers by points: if/else 4 (9 pts), boolean chains 4, loops 2 (3 pts), ternaries 1 (3 pts) (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Board/BoardOrderViewModel.swift"},"region":{"startLine":108}}}],"partialFingerprints":{"codehealthFindingId/v1":"bcd7602862992804d75b4c102ab635d946b5fad830673252cdcfe5e4b1962ddd"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.validateSimulationResultInternal (cognitive 18): BobsBuddyInvoker.validateSimulationResultInternal has cognitive complexity 18 (threshold 15). Drivers by points: if/else 10 (13 pts), boolean chains 5 (nesting depth added 3). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":640}}}],"partialFingerprints":{"codehealthFindingId/v1":"422cbe975a70299b1673b9083998e5e6c4d3574d5ff9a38c71ae7d27b74e16dc"}},{"ruleId":"D2","level":"warning","message":{"text":"RealmHelper.autoImportArena (cognitive 18): RealmHelper.autoImportArena has cognitive complexity 18 (threshold 15). Drivers by points: if/else 5 (8 pts), loops 2 (6 pts), boolean chains 2, error handling 1 (2 pts) (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/RealmHelper.swift"},"region":{"startLine":328}}}],"partialFingerprints":{"codehealthFindingId/v1":"e6a33b532b3e0b1210bf0ba6e7bc84646b2eeb4b3d671b66b5007ae77050e10d"}},{"ruleId":"D2","level":"warning","message":{"text":"RealmHelper.add (cognitive 18): RealmHelper.add has cognitive complexity 18 (threshold 15). Drivers by points: if/else 3 (9 pts), loops 3 (8 pts), error handling 1 (nesting depth added 11). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/RealmHelper.swift"},"region":{"startLine":511}}}],"partialFingerprints":{"codehealthFindingId/v1":"990ced958928026f1c9842eeee3cf452e7f17bccc8f306166637132ebd6884be"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.getRelatedCards (cognitive 18): Game.getRelatedCards has cognitive complexity 18 (threshold 15). Drivers by points: if/else 6 (7 pts), ternaries 3 (7 pts), boolean chains 2, loops 1 (2 pts) (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5051}}}],"partialFingerprints":{"codehealthFindingId/v1":"b71c5b4c7cc4cdebc7cdffc50846aa8b488b778a6e672c1baff13fa8fdd7be2c"}},{"ruleId":"D2","level":"warning","message":{"text":"AnachronosTurnCounter.handleTagChange (cognitive 18): AnachronosTurnCounter.handleTagChange has cognitive complexity 18 (threshold 15). Drivers by points: if/else 8 (11 pts), boolean chains 6, ternaries 1 (nesting depth added 3). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AnachronosTurnCounter.swift"},"region":{"startLine":77}}}],"partialFingerprints":{"codehealthFindingId/v1":"045a4e8166b025ed8d4c756a594022c4594cf80ea0282809e97b71d827a7c222"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsMinionArtView.body (cognitive 18): BattlegroundsMinionArtView.body has cognitive complexity 18 (threshold 15). Drivers by points: if/else 9, ternaries 6 (8 pts), boolean chains 1 (nesting depth added 2). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/BattlegroundsMinionArtView.swift"},"region":{"startLine":126}}}],"partialFingerprints":{"codehealthFindingId/v1":"d08d178b7a45454aee0b60b04eaabf7121d81544b393c8c116cb0268dbb1215e"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsQuestPickingViewModel.update (cognitive 18): BattlegroundsQuestPickingViewModel.update has cognitive complexity 18 (threshold 15). Drivers by points: if/else 10 (11 pts), error handling 2 (3 pts), boolean chains 2, loops 1, ternaries 1 (nesting depth added 2). To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/QuestPicking/BattlegroundsQuestPickingViewModel.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c6b0633fe543e008af5f026ac9482ced060ae943d27f5cab99544b1f5ecf1d2"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.getLastLethalResult (cognitive 17): BobsBuddyInvoker.getLastLethalResult has cognitive complexity 17 (threshold 15). Drivers by points: if/else 7 (9 pts), ternaries 2 (6 pts), boolean chains 2 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":607}}}],"partialFingerprints":{"codehealthFindingId/v1":"5007af9ad973058ccd57007f19639840ce502b44db703bfd0b17b8f2102929a3"}},{"ruleId":"D2","level":"warning","message":{"text":"HSReplayAPI.startAuthorizedRequest (cognitive 17): HSReplayAPI.startAuthorizedRequest has cognitive complexity 17 (threshold 15). Drivers by points: match/switch 4 (10 pts), if/else 4 (7 pts) (nesting depth added 9). To reduce it, keep the dispatch but shrink the arms: move each non-trivial case body into its own named function (or onto the value being matched) so the dispatch reads one line per case, and group related cases into a sub-dispatch. Keep every case explicit, and make the behaviour for cases you do not list a deliberate choice rather than an accident."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":110}}}],"partialFingerprints":{"codehealthFindingId/v1":"a7eb3f1e535e9c7b25e2a62a40e6eff5ac4071f05561c75f60060d9e0fe40401"}},{"ruleId":"D2","level":"warning","message":{"text":"DeckSerializer.serialize (cognitive 17): DeckSerializer.serialize has cognitive complexity 17 (threshold 15). Drivers by points: loops 6 (9 pts), if/else 6 (7 pts), ternaries 1 (nesting depth added 4). To reduce it, break up the iteration: give each loop body a named function, and split a multi-phase loop into one function per phase so no single body carries the whole pipeline."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Importers/DeckSerializer.swift"},"region":{"startLine":221}}}],"partialFingerprints":{"codehealthFindingId/v1":"2e43fa799d76fcf7e5891e586f68db85ead15125a0d991134c2a9c9830a2e6dc"}},{"ruleId":"D2","level":"warning","message":{"text":"CoreManager.internalStartTracking (cognitive 17): CoreManager.internalStartTracking has cognitive complexity 17 (threshold 15). Drivers by points: if/else 7 (13 pts), match/switch 1 (3 pts), boolean chains 1 (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":265}}}],"partialFingerprints":{"codehealthFindingId/v1":"045312bbe635f503cb399af7750346e66bce883929efc18d31e365e1de5de32f"}},{"ruleId":"D2","level":"warning","message":{"text":"EntityInfo.description (cognitive 17): EntityInfo.description has cognitive complexity 17 (threshold 15). Drivers by points: if/else 16, boolean chains 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/EntityInfo.swift"},"region":{"startLine":132}}}],"partialFingerprints":{"codehealthFindingId/v1":"713a7bb29d54a9eb87e597f82792da65d6e01cfbb5aaed144f7ea4b68d8bf8be"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.getBattlegroundsHeroPickStats (cognitive 17): Game.getBattlegroundsHeroPickStats has cognitive complexity 17 (threshold 15). Drivers by points: if/else 10 (12 pts), ternaries 3, boolean chains 2 (nesting depth added 2). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3161}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd6581a26261e6b6a0327778cc49bf5c8e63c2df618998ce592110eee3b059b4"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.getMulliganV2Data (cognitive 17): Game.getMulliganV2Data has cognitive complexity 17 (threshold 15). Drivers by points: if/else 10 (13 pts), boolean chains 3, ternaries 1 (nesting depth added 3). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4795}}}],"partialFingerprints":{"codehealthFindingId/v1":"b7296182a753c9b176c1875d44bd60f4193e1446e095eee4c046027457ab94a5"}},{"ruleId":"D2","level":"warning","message":{"text":"LogReaderManager.processLine (cognitive 17): LogReaderManager.processLine has cognitive complexity 17 (threshold 15). Drivers by points: if/else 9 (15 pts), boolean chains 1, match/switch 1 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/LogReaderManager.swift"},"region":{"startLine":211}}}],"partialFingerprints":{"codehealthFindingId/v1":"5353a4f710ab1c5dcc53fec0f0980f4f900f4718d6a841ec1f0a3fc7d7a49f28"}},{"ruleId":"D2","level":"warning","message":{"text":"ReflectionHelper.initialize (cognitive 17): ReflectionHelper.initialize has cognitive complexity 17 (threshold 15). Drivers by points: if/else 7 (10 pts), boolean chains 6, loops 1 (nesting depth added 3). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/ReflectionHelper.swift"},"region":{"startLine":66}}}],"partialFingerprints":{"codehealthFindingId/v1":"0303d1c87be2d4a0d0b2124c84abc59bf7a94926975cc70b6a7a311ce0accebe"}},{"ruleId":"D2","level":"warning","message":{"text":"SecretsManager.entityDamage (cognitive 17): SecretsManager.entityDamage has cognitive complexity 17 (threshold 15). Drivers by points: if/else 8 (13 pts), boolean chains 4 (nesting depth added 5). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":910}}}],"partialFingerprints":{"codehealthFindingId/v1":"e59779301598dc569ac61e9d6896f126980a89b945a63a7c8bdfebb80a6081c8"}},{"ruleId":"D2","level":"warning","message":{"text":"TagChangeActions.linkedEntity (cognitive 17): TagChangeActions.linkedEntity has cognitive complexity 17 (threshold 15). Drivers by points: boolean chains 12, if/else 5. To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":353}}}],"partialFingerprints":{"codehealthFindingId/v1":"9c4c770e36b0c0f4f44f5f885c2a77a47ddd7799c8a74ad38f5c6483dc6d0665"}},{"ruleId":"D2","level":"warning","message":{"text":"BoardCard.init (cognitive 17): BoardCard.init has cognitive complexity 17 (threshold 15). Drivers by points: if/else 7 (10 pts), ternaries 4, boolean chains 3 (nesting depth added 3). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BoardDamage/BoardCard.swift"},"region":{"startLine":49}}}],"partialFingerprints":{"codehealthFindingId/v1":"6234983e043f338aacc2fd37593aeecbd5e2eaecfd2d6826423b72f58876d5ad"}},{"ruleId":"D2","level":"warning","message":{"text":"SelfDamageCounter.handleTagChange (cognitive 17): SelfDamageCounter.handleTagChange has cognitive complexity 17 (threshold 15). Drivers by points: if/else 10 (12 pts), boolean chains 3, ternaries 2 (nesting depth added 2). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/SelfDamageCounter.swift"},"region":{"startLine":54}}}],"partialFingerprints":{"codehealthFindingId/v1":"d267c62b8cba5d547b3a9bce29c57682ac62ee55f36c42dfe18d64f2fcc8c069"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsSessionViewModel.getBattlegroundsCompStats (cognitive 17): BattlegroundsSessionViewModel.getBattlegroundsCompStats has cognitive complexity 17 (threshold 15). Drivers by points: if/else 11 (14 pts), boolean chains 2, loops 1 (nesting depth added 3). To reduce it, split the body: most of this score is breadth rather than depth \u2014 checks laid out side by side rather than stacked \u2014 so group the statements between the checks into named steps and move each step into its own function. Some of it IS depth: where a check sits inside another whose only job is to reach it, merge the two into one condition, and where an else follows a branch that already returns, drop the trailing else and let the rest of the body continue at one level."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift"},"region":{"startLine":355}}}],"partialFingerprints":{"codehealthFindingId/v1":"3d0dada76cd690f7251d85186453b81b085478d8c87802ceb3d3c4a83c3829bb"}},{"ruleId":"D2","level":"warning","message":{"text":"BobsBuddyInvoker.reconcileAutoAssemblerDeathrattles (cognitive 16): BobsBuddyInvoker.reconcileAutoAssemblerDeathrattles has cognitive complexity 16 (threshold 15). Drivers by points: if/else 7, boolean chains 5, ternaries 3, loops 1. To reduce it, split the body: this score is breadth rather than depth \u2014 many checks laid out side by side rather than nested inside one another, so inverting conditions into early returns has nothing left to flatten. Group the statements between the checks into named steps and move each step into its own function, so the body reads as a short sequence of named stages."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":2244}}}],"partialFingerprints":{"codehealthFindingId/v1":"b313fe54921ade4c45ff8d64f230ba8f06b1d9f3de477f5e57c838dba3152065"}},{"ruleId":"D2","level":"warning","message":{"text":"HSReplayNetHelper.syncCollection (cognitive 16): HSReplayNetHelper.syncCollection has cognitive complexity 16 (threshold 15). Drivers by points: if/else 9 (13 pts), boolean chains 2, error handling 1 (nesting depth added 4). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body. This shape REPEATS in the file: one other method here (HSReplayNetHelper.syncMercenariesCollection) has the same decision points, in the same order, at the same nesting depths \u2014 so this is one pattern written twice rather than two separate problems. Splitting this body alone leaves the other exactly as it is. Where these are variations on one operation, the change that clears both is the shared one: lift the common shape into a single routine the variants call, parameterised by whatever genuinely differs between them, and keep in each method only the part that is not shared."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayNetHelper.swift"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"74aca02cc2f73223f3ae269b0e8ba273399aab44571af133236b33e8b7d43eba"}},{"ruleId":"D2","level":"warning","message":{"text":"HSReplayNetHelper.syncMercenariesCollection (cognitive 16): HSReplayNetHelper.syncMercenariesCollection has cognitive complexity 16 (threshold 15). Drivers by points: if/else 9 (13 pts), boolean chains 2, error handling 1 (nesting depth added 4). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body. This shape REPEATS in the file: one other method here (HSReplayNetHelper.syncCollection) has the same decision points, in the same order, at the same nesting depths \u2014 so this is one pattern written twice rather than two separate problems. Splitting this body alone leaves the other exactly as it is. Where these are variations on one operation, the change that clears both is the shared one: lift the common shape into a single routine the variants call, parameterised by whatever genuinely differs between them, and keep in each method only the part that is not shared."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayNetHelper.swift"},"region":{"startLine":80}}}],"partialFingerprints":{"codehealthFindingId/v1":"12fc9a39f602696e4e66b4563fa4e4612e261fe63d5ab22673f5a21328ff19e0"}},{"ruleId":"D2","level":"warning","message":{"text":"HearthDbConverter.toHearthDbDeck (cognitive 16): HearthDbConverter.toHearthDbDeck has cognitive complexity 16 (threshold 15). Drivers by points: if/else 3 (9 pts), loops 2 (6 pts), boolean chains 1 (nesting depth added 10). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/HearthDbConverter.swift"},"region":{"startLine":29}}}],"partialFingerprints":{"codehealthFindingId/v1":"36114bff4fb2ffac4791b335b7a9b53e13dbe561c141d7fb697c139e981a02d9"}},{"ruleId":"D2","level":"warning","message":{"text":"Game.playerStolen (cognitive 16): Game.playerStolen has cognitive complexity 16 (threshold 15). Drivers by points: if/else 7 (16 pts) (nesting depth added 9). To reduce it, flatten the nesting: this score is depth rather than breadth \u2014 most of its points come from checks stacked inside one another, so the work sits several levels in. Invert each enclosing check into an early exit (a return, or the language\u0027s equivalent) so the happy path stays at one level, and where a level cannot be exited early, lift the block it encloses into its own named function."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3587}}}],"partialFingerprints":{"codehealthFindingId/v1":"4eb8e5e251c9cc765c17310dd7a089aa55b1ea486d591c486807f6a6fc7c2131"}},{"ruleId":"D2","level":"warning","message":{"text":"ChoicesHandler.flush (cognitive 16): ChoicesHandler.flush has cognitive complexity 16 (threshold 15). Drivers by points: if/else 9 (16 pts) (nesting depth added 7). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/ChoicesHandler.swift"},"region":{"startLine":106}}}],"partialFingerprints":{"codehealthFindingId/v1":"97080be0dceb64b863971990ac0e60de08c0d3bacae07dc7660c9380b82a5430"}},{"ruleId":"D2","level":"warning","message":{"text":"BoardCard.isAbleToAttack (cognitive 16): BoardCard.isAbleToAttack has cognitive complexity 16 (threshold 15). Drivers by points: boolean chains 11, if/else 5. To reduce it, name the conditions: bind each compound test to a well-named local or a small predicate function, so the body reads as a sequence of named decisions rather than a chain of operators."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BoardDamage/BoardCard.swift"},"region":{"startLine":113}}}],"partialFingerprints":{"codehealthFindingId/v1":"133a5bff217370ce3b32d87769b0f692b8cb3837ed18aa3cd968e42bcba61448"}},{"ruleId":"D2","level":"warning","message":{"text":"BoardHero.attackWithWeapon (cognitive 16): BoardHero.attackWithWeapon has cognitive complexity 16 (threshold 15). Drivers by points: if/else 5 (10 pts), boolean chains 6 (nesting depth added 5). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BoardDamage/BoardHero.swift"},"region":{"startLine":52}}}],"partialFingerprints":{"codehealthFindingId/v1":"23c49e7612a79db7da0279a4b0a7ff3dfa131b287781f664473c10032a38a494"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsSessionViewModel.getSessionGames (cognitive 16): BattlegroundsSessionViewModel.getSessionGames has cognitive complexity 16 (threshold 15). Drivers by points: if/else 7 (12 pts), boolean chains 3, loops 1 (nesting depth added 5). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift"},"region":{"startLine":514}}}],"partialFingerprints":{"codehealthFindingId/v1":"fdb6b6be6e16b4884c7ed8e7d4bfb11eebc079e680c848e7d36cab3f8986dac7"}},{"ruleId":"D2","level":"warning","message":{"text":"CardTileView.content (cognitive 16): CardTileView.content has cognitive complexity 16 (threshold 15). Drivers by points: if/else 10 (15 pts), boolean chains 1 (nesting depth added 5). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Trackers/CardTileView.swift"},"region":{"startLine":64}}}],"partialFingerprints":{"codehealthFindingId/v1":"d5463e9d96b90f7a9915a01f47854364350baa3cdbd6ab4353eea7b1415879cb"}},{"ruleId":"D2","level":"warning","message":{"text":"BattlegroundsCompGuideViewModel.pinnableCardIds (cognitive 16): BattlegroundsCompGuideViewModel.pinnableCardIds has cognitive complexity 16 (threshold 15). Drivers by points: if/else 4 (7 pts), loops 3 (6 pts), boolean chains 2, ternaries 1 (nesting depth added 6). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Comps/BattlegroundsCompGuideViewModel.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"bb814dd1e61ae714f34885162be99eb36dd4a35ab50b1b8db9cbf178985e217a"}},{"ruleId":"D2","level":"warning","message":{"text":"MercenariesAbility.swift.getMercAbilities (cognitive 16): MercenariesAbility.swift.getMercAbilities has cognitive complexity 16 (threshold 15). Drivers by points: if/else 3 (6 pts), ternaries 3 (5 pts), boolean chains 4, loops 1 (nesting depth added 5). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Board/MercenariesAbility.swift"},"region":{"startLine":34}}}],"partialFingerprints":{"codehealthFindingId/v1":"7683100bcb74949918fe21e8cc71e44139d707e62c08eee811634b535aaae577"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: PowerGameStateParser.handle: MethodTooLong \u2014 handle runs 1254 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 1154 over it, 12.54\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"432af1717691b747c434d0af08345b0c6ef5607a9fdeaf383bc6bc515de14ba4"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Game: TooManyMethods \u2014 287 methods. The bar is 30 methods; this is 257 over it, 9.57\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":36}}}],"partialFingerprints":{"codehealthFindingId/v1":"687bc153708afa65dc1a73690790fa8eb5bf7abb0a059e22381297459e445799"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: Game: ClassTooLong \u2014 3198 significant lines (blank, comment-only and punctuation-only lines excluded), 287 methods. The bar is 400 significant lines; this is 2798 over it, 8.00\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":36}}}],"partialFingerprints":{"codehealthFindingId/v1":"129941b3eaba637ca53ffca2bf6ac04c455d521a2145e4d3aac70219d84cd833"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Logging/Game.swift: FileTooLong \u2014 3215 significant lines (blank, comment-only and punctuation-only lines excluded), about 99% of them inside a single declaration: Game (36-5543). The bar is 500 significant lines; this is 2715 over it, 6.43\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"66771564e1acc923ff68c62d44ab87128fdedae53cbc7dc5d28f33955ede1f4b"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: BobsBuddyInvoker: ClassTooLong \u2014 1459 significant lines (blank, comment-only and punctuation-only lines excluded), 72 methods. The bar is 400 significant lines; this is 1059 over it, 3.65\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"9dbe82b092f111299929fceab42b231e471453540ed3ca80debed663cf10e8a7"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: PowerGameStateParser: ClassTooLong \u2014 1353 significant lines (blank, comment-only and punctuation-only lines excluded), 14 methods. The bar is 400 significant lines; this is 953 over it, 3.38\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"226e0e959e8256d0320d71a805329aee90077f34a6234f464e1634058632d695"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: BobsBuddy/BobsBuddyInvoker.swift: FileTooLong \u2014 1469 significant lines (blank, comment-only and punctuation-only lines excluded), about 99% of them inside a single declaration: BobsBuddyInvoker (26-2437). The bar is 500 significant lines; this is 969 over it, 2.94\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"2ada71b5f0b9b854f31b75e306598a5f6b91ecdb3e7822793454ed1d11882b20"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Parsers/PowerGameStateParser.swift: FileTooLong \u2014 1356 significant lines (blank, comment-only and punctuation-only lines excluded), about 100% of them inside a single declaration: PowerGameStateParser (17-1822). The bar is 500 significant lines; this is 856 over it, 2.71\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"61313124e91cb32572e77335a4b17e9e5289f6d1ac690f4208dcdf9bdaf28bf5"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: TagChangeActions: ClassTooLong \u2014 1020 significant lines (blank, comment-only and punctuation-only lines excluded), 71 methods. The bar is 400 significant lines; this is 620 over it, 2.55\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"1054ea41161dcce4e9c2e53a49d85a9fd85640a092a9bf402df3864e25fe091c"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: BobsBuddyInvoker: TooManyMethods \u2014 72 methods. The bar is 30 methods; this is 42 over it, 2.40\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"f441cd8d3db4e579d71d6c19bd3c76c1fd67bf99ee2fd034a0ec30df89fdebe4"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: TagChangeActions: TooManyMethods \u2014 71 methods. The bar is 30 methods; this is 41 over it, 2.37\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"a7f948ecb659dd8a02e532b171a08dbd2857e0031d8afdf1c44050b402924a30"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: HSReplayAPI: ClassTooLong \u2014 919 significant lines (blank, comment-only and punctuation-only lines excluded), 56 methods. The bar is 400 significant lines; this is 519 over it, 2.30\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"c057e48cc049500feae39ace5940e56cfc247a37c855d50d60452edcf9e19a81"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: BobsBuddyInvoker.setupInputPlayer: MethodTooLong \u2014 setupInputPlayer runs 217 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 117 over it, 2.17\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1171}}}],"partialFingerprints":{"codehealthFindingId/v1":"227a8e25d01969b00ba81363e838835973054953ec830cb09553a737fe82b8cc"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: MirrorHelper: TooManyMethods \u2014 63 methods. The bar is 30 methods; this is 33 over it, 2.10\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthMirror/MirrorHelper.swift"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"fe634b5d1f19c95073e0b9966ebcf0484a93fdace95dd3ad06f2b0b345a3139c"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Parsers/TagChangeActions.swift: FileTooLong \u2014 1021 significant lines (blank, comment-only and punctuation-only lines excluded), about 100% of them inside a single declaration: TagChangeActions (11-1634). The bar is 500 significant lines; this is 521 over it, 2.04\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca2c018214b2f578f8ac1d5044ea69d4f8709e2f99c44dd51ea302853c441a70"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: Player: ClassTooLong \u2014 775 significant lines (blank, comment-only and punctuation-only lines excluded), 52 methods. The bar is 400 significant lines; this is 375 over it, 1.94\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":94}}}],"partialFingerprints":{"codehealthFindingId/v1":"c265100da57f228c4195bba6ccc995d465b5015143b049164546838fb32cb5f0"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: RootOverlayView.canvas: MethodTooLong \u2014 canvas runs 189 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 89 over it, 1.89\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayView.swift"},"region":{"startLine":151}}}],"partialFingerprints":{"codehealthFindingId/v1":"57871a2912131ff2ab625720853725fc8d799595f8b41b9ef513f84c002d7487"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: HSReplayAPI: TooManyMethods \u2014 56 methods. The bar is 30 methods; this is 26 over it, 1.87\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"15fff3839f334ceb54df0966e14557588f8ae63a72aa1874eb57d613eab1f2e0"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: HSReplay/HSReplayAPI.swift: FileTooLong \u2014 932 significant lines (blank, comment-only and punctuation-only lines excluded), about 99% of them inside a single declaration: HSReplayAPI (27-1440). The bar is 500 significant lines; this is 432 over it, 1.86\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"0d942cf537e2757299e8ecc91758d3b1e4fc1a3a6fd2471e8d74d8c430828f13"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Player: TooManyMethods \u2014 52 methods. The bar is 30 methods; this is 22 over it, 1.73\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":94}}}],"partialFingerprints":{"codehealthFindingId/v1":"9f337a7e4ae11576a36fa1c8f81407334ee052cd6d343925ee7ae795c030aaa5"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: DefaultDecks: ClassTooLong \u2014 674 significant lines (blank, comment-only and punctuation-only lines excluded), 0 methods. The bar is 400 significant lines; this is 274 over it, 1.69\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CardIds/DefaultDecks.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"964bdd8f4d127a01fdf30d89a8e2877f919c0aeef9c3795bf383b13c0a5fb398"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Logging/Player.swift: FileTooLong \u2014 839 significant lines (blank, comment-only and punctuation-only lines excluded), about 92% of them inside a single declaration: Player (94-1262). The bar is 500 significant lines; this is 339 over it, 1.68\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"ad3e5762aecbb6da418e073698e4faf5728cd0a785b0dc98f7b0de042bc87007"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: CardBar: TooManyMethods \u2014 50 methods. The bar is 30 methods; this is 20 over it, 1.67\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"21d5bef4275b1b9b87708f35cadc09bb3feab46ec889e2bf979e9a8215ab4e3d"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Core/Settings.swift: FileTooLong \u2014 817 significant lines (blank, comment-only and punctuation-only lines excluded), about 63% of them inside a single declaration: Settings (130-874). The bar is 500 significant lines; this is 317 over it, 1.63\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/Settings.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f3d02746976d13f475841b7cce75c87bcbf2086d1ef3feaf53a3d4dae3471bd9"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: DeckManager: TooManyMethods \u2014 48 methods. The bar is 30 methods; this is 18 over it, 1.60\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/DeckManager.swift"},"region":{"startLine":30}}}],"partialFingerprints":{"codehealthFindingId/v1":"b13fecf1eb0c70233a0c7978ef1d5b7245410f52648eae897b752e97e3bd0568"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: SecretsManager: TooManyMethods \u2014 45 methods. The bar is 30 methods; this is 15 over it, 1.50\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"ad06f123575947e54247f2f7bd5639fb45cc609d87e35cebc7fdbde971290143"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: CardBar: ClassTooLong \u2014 585 significant lines (blank, comment-only and punctuation-only lines excluded), 50 methods. The bar is 400 significant lines; this is 185 over it, 1.46\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"ce2eb30a5d1a9fcc7894f2759546e7c9439d94bc7ff20860816bafb9172a9677"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: SecretsManager: ClassTooLong \u2014 575 significant lines (blank, comment-only and punctuation-only lines excluded), 45 methods. The bar is 400 significant lines; this is 175 over it, 1.44\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"14fe1778c6b0a31f5f4d385f1e2355eb2aac797332d323f11b504e35e8efc87a"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: MonoHelper: TooManyMethods \u2014 42 methods. The bar is 30 methods; this is 12 over it, 1.40\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":293}}}],"partialFingerprints":{"codehealthFindingId/v1":"d588016488779d89d44f4ff84f3888a99e732a62c437f5ad2376d178e08a79a8"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: EditDeck: TooManyMethods \u2014 42 methods. The bar is 30 methods; this is 12 over it, 1.40\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/EditDeck.swift"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"46cfa468d508b06cbdbeb3c04738f0d00c37310d3419906bfdf4d99f6cb1088a"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: CoreManager: ClassTooLong \u2014 558 significant lines (blank, comment-only and punctuation-only lines excluded), 33 methods. The bar is 400 significant lines; this is 158 over it, 1.40\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":29}}}],"partialFingerprints":{"codehealthFindingId/v1":"b0359ebc0f6fe278a59ceb04fd7b5f3ad8c32259e97f7266994e95b62e288a61"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: CardIds/DefaultDecks.swift: FileTooLong \u2014 675 significant lines (blank, comment-only and punctuation-only lines excluded), about 100% of them inside a single declaration: DefaultDecks (11-770). The bar is 500 significant lines; this is 175 over it, 1.35\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CardIds/DefaultDecks.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"edfc15b85a988599f96644a8773e31c01cea628a77ffebacf777c8e62c6e0d33"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Mono/MonoHelper.swift: FileTooLong \u2014 660 significant lines (blank, comment-only and punctuation-only lines excluded), about 73% of them inside a single declaration: MonoHelper (293-1099). The bar is 500 significant lines; this is 160 over it, 1.32\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"72e41fd6f59de5857f58645ce7bc395e64c36f1c95cfa4080a56e268a9d7f69c"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: AppDelegate: ClassTooLong \u2014 521 significant lines (blank, comment-only and punctuation-only lines excluded), 34 methods. The bar is 400 significant lines; this is 121 over it, 1.30\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":20}}}],"partialFingerprints":{"codehealthFindingId/v1":"d7de5d37099267968ad76db0de596d7dd7744ad62d4f6a0166ac4e3075b3c375"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: Settings: ClassTooLong \u2014 513 significant lines (blank, comment-only and punctuation-only lines excluded), 3 methods. The bar is 400 significant lines; this is 113 over it, 1.28\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/Settings.swift"},"region":{"startLine":130}}}],"partialFingerprints":{"codehealthFindingId/v1":"a5bf4918b886c8ab428d6febc0b707e5cf9f97f0bec6c3d8804c11e164ed9f58"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: ArenaPickHelperViewModel: ClassTooLong \u2014 511 significant lines (blank, comment-only and punctuation-only lines excluded), 26 methods. The bar is 400 significant lines; this is 111 over it, 1.28\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPickHelperViewModel.swift"},"region":{"startLine":104}}}],"partialFingerprints":{"codehealthFindingId/v1":"b0ac25759a59133b95249e8509d6c266c379db6794e37c49a52435c52da48423"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Card: TooManyMethods \u2014 37 methods. The bar is 30 methods; this is 7 over it, 1.23\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Models/Card.swift"},"region":{"startLine":21}}}],"partialFingerprints":{"codehealthFindingId/v1":"80a16c167e1f21e888580f65d001459bc30f58fcf45d17f5d9a178decf3719f4"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: TagChangeActions.findAction: MethodTooLong \u2014 findAction runs 121 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 21 over it, 1.21\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":21}}}],"partialFingerprints":{"codehealthFindingId/v1":"b06e03474d30b960084b834ce85961a9dfa7b3d9de908223802530ba33f1e9c9"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Cards/CardBar.swift: FileTooLong \u2014 604 significant lines (blank, comment-only and punctuation-only lines excluded), about 97% of them inside a single declaration: CardBar (25-895). The bar is 500 significant lines; this is 104 over it, 1.21\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"32a06f31028843f0d3e639c719e31889195096ea7c255e20f5a0a1e28678dad7"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: MonoHelper: ClassTooLong \u2014 481 significant lines (blank, comment-only and punctuation-only lines excluded), 42 methods. The bar is 400 significant lines; this is 81 over it, 1.20\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":293}}}],"partialFingerprints":{"codehealthFindingId/v1":"e1ad84e781cc88cb18eedd7d2ab604a53ae39cd3d2a197c653a5e75c7d049355"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: DeckManager/DeckManager.swift: FileTooLong \u2014 578 significant lines (blank, comment-only and punctuation-only lines excluded), about 79% of them inside a single declaration: DeckManager (30-734). The bar is 500 significant lines; this is 78 over it, 1.16\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/DeckManager.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f9f1ebb4a4f06cbff231488e49df4db2baa83819b238773f69714c18202a2948"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Secrets/SecretsManager.swift: FileTooLong \u2014 576 significant lines (blank, comment-only and punctuation-only lines excluded), about 100% of them inside a single declaration: SecretsManager (11-940). The bar is 500 significant lines; this is 76 over it, 1.15\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"0cfa726cefb7523736ec54e3e996003700157b52df59f1b5f49dde6d12e257c9"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: BobsBuddyInvoker.getMinionFromEntity: MethodTooLong \u2014 getMinionFromEntity runs 115 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 15 over it, 1.15\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":781}}}],"partialFingerprints":{"codehealthFindingId/v1":"6c6013238594becd52c92c87237703236022caf537b28ba6d244ef428432909c"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: DeckManager: ClassTooLong \u2014 457 significant lines (blank, comment-only and punctuation-only lines excluded), 48 methods. The bar is 400 significant lines; this is 57 over it, 1.14\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/DeckManager.swift"},"region":{"startLine":30}}}],"partialFingerprints":{"codehealthFindingId/v1":"9847ad28cfd16ef46f356446d81ffada4b71279465eb8b5c7f5187fab4a1461c"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Logging/CoreManager.swift: FileTooLong \u2014 570 significant lines (blank, comment-only and punctuation-only lines excluded), about 98% of them inside a single declaration: CoreManager (29-893). The bar is 500 significant lines; this is 70 over it, 1.14\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"4af9172d83d09511b83caae9320fe816365f816641c4a27d0a514fde86e18958"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Game.tooltipDisplay: MethodTooLong \u2014 tooltipDisplay runs 114 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 14 over it, 1.14\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5105}}}],"partialFingerprints":{"codehealthFindingId/v1":"a5e702278c61e08340b1ab37f5ff8df76f3580e242541b011679fa15930b8710"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: AppDelegate: TooManyMethods \u2014 34 methods. The bar is 30 methods; this is 4 over it, 1.13\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":20}}}],"partialFingerprints":{"codehealthFindingId/v1":"50c0d3b0f62698924c9b6af16f7aa8b8ac50f4c2be5a870ac189477bd2ac8639"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: Arena/ArenaPickHelperViewModel.swift: FileTooLong \u2014 564 significant lines (blank, comment-only and punctuation-only lines excluded), about 91% of them inside a single declaration: ArenaPickHelperViewModel (104-931). The bar is 500 significant lines; this is 64 over it, 1.13\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPickHelperViewModel.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"3aa53eee1360f94ba1e718baac4448e3fa8cabbcb3088b11e9bb389092f60d88"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: CoreManager: TooManyMethods \u2014 33 methods. The bar is 30 methods; this is 3 over it, 1.10\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":29}}}],"partialFingerprints":{"codehealthFindingId/v1":"2f1bd35799262a304a2972524d371f554664f5196c9b60ae8d4a090fcbe3001b"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: BobsBuddyPanelViewModel: TooManyMethods \u2014 33 methods. The bar is 30 methods; this is 3 over it, 1.10\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BobsBuddy/BobsBuddyPanelViewModel.swift"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"7ba2c51895849ed2d6692ee5877365385fb33f266ba0434f4d0c1da564dd1186"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: BobsBuddyInvoker.runSimulation: MethodTooLong \u2014 runSimulation runs 109 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 9 over it, 1.09\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":369}}}],"partialFingerprints":{"codehealthFindingId/v1":"50fa2a9dc01b7ce605278ce5f08d1e91dc871fcff1c3e9cac2c9d7a2dc171905"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: BattlegroundsPreferences.checkboxClicked: MethodTooLong \u2014 checkboxClicked runs 107 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 7 over it, 1.07\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/BattlegroundsPreferences.swift"},"region":{"startLine":106}}}],"partialFingerprints":{"codehealthFindingId/v1":"3101ad44013d6c3395dbe262d74a09f3736781d2e64f606176a7da203bcce659"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: HSTracker/AppDelegate.swift: FileTooLong \u2014 531 significant lines (blank, comment-only and punctuation-only lines excluded), about 98% of them inside a single declaration: AppDelegate (21-889). The bar is 500 significant lines; this is 31 over it, 1.06\u00D7 the bar. Moving the declarations that sit BESIDE it into sibling files will not shorten this file. Extract from INSIDE that declaration instead: lift each cohesive group of its body \u2014 the parts that share the same inputs and are named together \u2014 into its own unit in a sibling file, and have the original call them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"dc114ce5a93f58245723fedef3c52cab07615675103a98f3ff6c112cab3e0f65"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: BattlegroundsMinionPinningViewModel: TooManyMethods \u2014 31 methods. The bar is 30 methods; this is 1 over it, 1.03\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningViewModel.swift"},"region":{"startLine":34}}}],"partialFingerprints":{"codehealthFindingId/v1":"68921f69d7cc1fad2972f52ec48a7b34f36c975c3ea1e718711a04b958388e20"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: AppDelegate.applicationDidFinishLaunching: MethodTooLong \u2014 applicationDidFinishLaunching runs 101 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 1 over it, 1.01\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":82}}}],"partialFingerprints":{"codehealthFindingId/v1":"3289c72ea7fbe55d2014e079768bfae4e2e75e8f669aff777164a8b14235978b"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Game.generateEndgameStatistics: MethodTooLong \u2014 generateEndgameStatistics runs 101 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 1 over it, 1.01\u00D7 the bar. This is length, not branching: a long straight-line body scores low on complexity and is still read whole to change any part of it, so the complexity numbers beside this row neither confirm nor excuse it. To reduce it, extract each cohesive step of the body \u2014 the runs of statements that work on the same values and would earn the same name \u2014 into its own named unit, and have this one call them in order."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2158}}}],"partialFingerprints":{"codehealthFindingId/v1":"c07229b5fdc3cb54453eccfc7205067d489ef288ee668121276888511566ada4"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (12 corresponding lines): HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift:100-113 | HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift:115-128 \u2014 These two members are one piece of code written twice and then edited apart: 12 consecutive lines correspond almost exactly, broken only by small local edits. Most of that correspondence is NOT reported as duplicated blocks below \u2014 the edits cut it into fragments and only the largest of them clear the block floor, so the rows below understate it. The repair is at the members\u0027 grain \u2014 factor the shared implementation into one the two call with their differences as parameters or as an injected step, or, where the difference is systematic (an extra return value, one transport against another), generate one from the other. Left alone, the next edit has to be made twice and the two will drift further apart."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift"},"region":{"startLine":100}}}],"partialFingerprints":{"codehealthFindingId/v1":"30b5df2b82c2837372298382dcddc8115fdfd41d74d67154a5827321c1592907"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (11 corresponding lines): HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift:163-173 | HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift:196-206 \u2014 These two members are one piece of code written twice and then edited apart: 11 consecutive lines correspond almost exactly, broken only by small local edits. Most of that correspondence is NOT reported as duplicated blocks below \u2014 the edits cut it into fragments and only the largest of them clear the block floor, so the rows below understate it. The repair is at the members\u0027 grain \u2014 factor the shared implementation into one the two call with their differences as parameters or as an injected step, or, where the difference is systematic (an extra return value, one transport against another), generate one from the other. Left alone, the next edit has to be made twice and the two will drift further apart."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift"},"region":{"startLine":163}}}],"partialFingerprints":{"codehealthFindingId/v1":"83d14e1509c2c5b1e72c5ce0a28aab63f4320340a6c4c1f74fa1daf9463ca656"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (9 members, 50\u002B identical tokens): HSTracker/HSReplay/HSReplayAPI.swift:515-541 | HSTracker/HSReplay/HSReplayAPI.swift:578-604 | HSTracker/HSReplay/HSReplayAPI.swift:641-666 | HSTracker/HSReplay/HSReplayAPI.swift:757-788 | HSTracker/HSReplay/HSReplayAPI.swift:790-821 | HSTracker/HSReplay/HSReplayAPI.swift:885-916 | HSTracker/HSReplay/HSReplayAPI.swift:918-949 | HSTracker/HSReplay/HSReplayAPI.swift:951-977 | HSTracker/HSReplay/HSReplayAPI.swift:1379-1405 \u2014 These 9 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 9 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 9 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":515}}}],"partialFingerprints":{"codehealthFindingId/v1":"190367ce9910d486f01e0f2414bcb5a8afdd8700b14a2a68eb57cabbeae419d2"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (7 members, 50\u002B identical tokens): HSTracker/HSReplay/HSReplayAPI.swift:543-576 | HSTracker/HSReplay/HSReplayAPI.swift:606-639 | HSTracker/HSReplay/HSReplayAPI.swift:668-701 | HSTracker/HSReplay/HSReplayAPI.swift:826-859 | HSTracker/HSReplay/HSReplayAPI.swift:979-1012 | HSTracker/HSReplay/HSReplayAPI.swift:1041-1072 | HSTracker/HSReplay/HSReplayAPI.swift:1407-1440 \u2014 These 7 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 7 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 7 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":543}}}],"partialFingerprints":{"codehealthFindingId/v1":"e92a91cc3a16bd6f049310e28070cd66b7c1e557d0ff4efcdc55c44eebedb7a7"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (5 members, 50\u002B identical tokens): HSTracker/Hearthstone/CounterSystem/BgCounters/AncestralAutomatonCounter.swift:63-80 | HSTracker/Hearthstone/CounterSystem/BgCounters/BeetlesSizeCounter.swift:54-73 | HSTracker/Hearthstone/CounterSystem/BgCounters/EternalKnightCounter.swift:57-73 | HSTracker/Hearthstone/CounterSystem/BgCounters/SpellsPlayedForNagasCounter.swift:46-59 | HSTracker/Hearthstone/CounterSystem/BgCounters/UndeadBuffCounter.swift:43-64 \u2014 These 5 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 5 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 5 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/AncestralAutomatonCounter.swift"},"region":{"startLine":63}}}],"partialFingerprints":{"codehealthFindingId/v1":"7e60dc599c4aa034208c9021b7dc9a3aec793e9b5a435317be1ebeec48526e32"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (5 members, 50\u002B identical tokens): HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift:64-79 | HSTracker/Hearthstone/CounterSystem/Counters/HolySpellsPlayedCounter.swift:44-69 | HSTracker/Hearthstone/CounterSystem/Counters/PaladinCardsPlayedCounter.swift:43-80 | HSTracker/Hearthstone/CounterSystem/Counters/PlayedSpellSchoolsCounter.swift:72-95 | HSTracker/Hearthstone/CounterSystem/Counters/SpellsPlayedInCharactersCounter.swift:44-70 \u2014 These 5 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 5 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 5 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift"},"region":{"startLine":64}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca047c47584615b3f9ec47ccad6a911483a26999cc6a5b6b2818dfd3ae33d0c7"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (5 members, 50\u002B identical tokens): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:33-59 | HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:68-87 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:43-95 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:100-120 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:125-153 \u2014 These 5 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 5 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 5 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift"},"region":{"startLine":33}}}],"partialFingerprints":{"codehealthFindingId/v1":"bffd9efdd33276cdd2594f5373919505d21500f62418d4063e1afbc329a23238"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (4 members, 50\u002B identical tokens): HSTracker/Database/RealmHelper.swift:467-486 | HSTracker/Database/RealmHelper.swift:488-509 | HSTracker/Database/RealmHelper.swift:511-542 | HSTracker/Database/RealmHelper.swift:544-558 \u2014 These 4 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 4 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 4 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/RealmHelper.swift"},"region":{"startLine":467}}}],"partialFingerprints":{"codehealthFindingId/v1":"9b196aba6bcef0bd78856be4364d7ce749828d02d6d686598a49a12f342c6953"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (4 members, 50\u002B identical tokens): HSTracker/Hearthstone/CounterSystem/BgCounters/BloodGemBarrageBuffCounter.swift:43-63 | HSTracker/Hearthstone/CounterSystem/BgCounters/RandomTavernMinionBuffCounter.swift:49-69 | HSTracker/Hearthstone/CounterSystem/BgCounters/VolumizerBuffCounter.swift:50-70 | HSTracker/Hearthstone/CounterSystem/BgCounters/WhelpStatsBuffCounter.swift:47-67 \u2014 These 4 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 4 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 4 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/BloodGemBarrageBuffCounter.swift"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"c5bc98182eddd512da4093984478113291e97f4d3d8fb8adefeb328ff812544d"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (4 members, 50\u002B identical tokens): HSTracker/Mono/Leapfrogger.swift:24-36 | HSTracker/Mono/ReplicatingMenace.swift:24-36 | HSTracker/Mono/Scallywag.swift:24-36 | HSTracker/Mono/WhirringProtector.swift:24-36 \u2014 These 4 members share a duplicated core: a run of at least 50 identical tokens appears in every one of them. That run is NOT broken out as duplicated-block rows below \u2014 it is what admitted this row, and the blocks below cover only the part of it that clears the block floor, so they understate the correspondence. Read the members as one construct written 4 times. The repair is at the members\u0027 grain \u2014 factor the shared implementation out once and have all of them call it with their differences as parameters or as an injected step, or, where the difference is systematic, generate them from one template. Extracting the individual blocks below is not the same fix: it leaves every body in place and the next edit still has to be made 4 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/Leapfrogger.swift"},"region":{"startLine":24}}}],"partialFingerprints":{"codehealthFindingId/v1":"16aa7c412d87fb718ea9de9c58f24c56b4d6802ca55856a113f14178b50d7463"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPreDraftView.swift:270-293 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:219-242 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":270}}}],"partialFingerprints":{"codehealthFindingId/v1":"026a038d90f1759feaa27bcb780ce229dfdfd8985270268550199bfc7826648f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (21\u201322 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPreDraftView.swift:298-318 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:153-174 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:298\u0060 it runs out through the closing brace of the declaration holding it and carries on into the declaration that follows \u2014 the window is the tail of one member plus the head of the next, so no call can be substituted for those exact lines, and the smallest declaration that contains all of them is the type they sit in. The repeated unit is the member each site sits in: where those members\u0027 bodies are the same, move one whole member to the shared location and have the others delegate to it; where the copies are a run of near-identical overloads or wrappers that differ only in their signatures, the repetition IS the run \u2014 a one-line delegation has no helper inside it to lift \u2014 so generate the run from the set it enumerates, or accept it and keep each member\u0027s own documentation with it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":298}}}],"partialFingerprints":{"codehealthFindingId/v1":"362ae1ad41a342f008b6418835011e9875e24b9f54f3abcd973312648783c8bb"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (21 lines \u00D7 2): HSTracker/Logging/Game.swift:5171-5191 | HSTracker/Logging/Game.swift:5220-5240 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5171}}}],"partialFingerprints":{"codehealthFindingId/v1":"1832492a7199877e2f28cd6520dd77f7f3d9212d533c7533b2aba9222fae320a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (20 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift:261-280 | HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift:359-378 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift:261\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The \u0060return\u0060 at the foot of the matched lines is the enclosing body\u0027s own terminal exit, not an early one: it moves with them unchanged, and each site calls the extracted unit from the position that \u0060return\u0060 occupied \u2014 no decision has to be handed back and re-acted on."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":261}}}],"partialFingerprints":{"codehealthFindingId/v1":"5d21e28444fe70016a71e778fa65b51611091f730343605ebf8ad3ec965251dc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 6): HSTracker/HSReplay/HSReplayAPI.swift:545-561 | HSTracker/HSReplay/HSReplayAPI.swift:608-624 | HSTracker/HSReplay/HSReplayAPI.swift:670-686 | HSTracker/HSReplay/HSReplayAPI.swift:828-844 | HSTracker/HSReplay/HSReplayAPI.swift:981-997 | HSTracker/HSReplay/HSReplayAPI.swift:1409-1425 \u2014 all 6 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:545\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":545}}}],"partialFingerprints":{"codehealthFindingId/v1":"d3390630ed2af5e32eb7d4c00cd5bd323f84f54958eb3b4375c71364dfefc706"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/CardsPlayedThisTurnCounter.swift:76-92 | HSTracker/Hearthstone/CounterSystem/Counters/CardsThatDidntStartInDeckPlayedCounter.swift:53-69 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/CardsPlayedThisTurnCounter.swift:76\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/CardsPlayedThisTurnCounter.swift"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"2744018fabd6f6ef537a9f6b0348d2cb1617197324c8fce84ff2227f2a36802e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/LeylineEffectIncreaseCounter.swift:39-55 | HSTracker/Hearthstone/CounterSystem/Counters/LeylineExtraTriggerCounter.swift:39-55 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/LeylineEffectIncreaseCounter.swift"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"14b5f9fe80b63efa3bacdddb1d5254e372b7b9f17093e2b126ea65e4d3ff2fba"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (3\u201317 lines \u00D7 3): HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift:90-105 | HSTracker/UIs/Battlegrounds/Guides/Quests/QuestGuideView.swift:65-81 | HSTracker/UIs/Tooltips/RelatedCardsBrowserPanel.swift:452-454 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift:90\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift"},"region":{"startLine":90}}}],"partialFingerprints":{"codehealthFindingId/v1":"f06a2a673fa43405b6c1799fe2fe631e5d8f9fbc827f264bcda9d47b6f16c38e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:93-109 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift:49-65 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift\u0060 and \u0060HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift\u0060 as WHOLE FILES: this scan already matched 5 separate duplicated blocks between them, totalling at least 46 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:93\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift"},"region":{"startLine":93}}}],"partialFingerprints":{"codehealthFindingId/v1":"dacafd92908f0dc63ac87bee610a940705c19b04f4a9e08e9985f7a168d4f184"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): HSTracker/Core/RegionDrawer.swift:361-376 | HSTracker/Core/RegionDrawer.swift:390-405 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Core/RegionDrawer.swift:361\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The \u0060return\u0060 at the foot of the matched lines is the enclosing body\u0027s own terminal exit, not an early one: it moves with them unchanged, and each site calls the extracted unit from the position that \u0060return\u0060 occupied \u2014 no decision has to be handed back and re-acted on."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/RegionDrawer.swift"},"region":{"startLine":361}}}],"partialFingerprints":{"codehealthFindingId/v1":"134062695d908e715bbf1cd45c7e41b03aae808dfed9fceda65c36db620e3738"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11\u201316 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/ElwynnBoarCounter.swift:55-70 | HSTracker/Hearthstone/CounterSystem/Counters/YsondreCounter.swift:53-63 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/ElwynnBoarCounter.swift:55\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ElwynnBoarCounter.swift"},"region":{"startLine":55}}}],"partialFingerprints":{"codehealthFindingId/v1":"af70efe4a12c949619ded1defebe7d7e3a76491cc9d7876f16158f0c27c5ae6f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): HSTracker/Mono/MinionProxy.swift:140-155 | HSTracker/Mono/MinionProxy.swift:158-173 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Each matched range is the entire body of the declaration above it, so the region is already a complete unit: move that whole declaration to the shared location and have each site call it, rather than lifting the lines out of their bodies. Any \u0060return\u0060 inside it is the body\u0027s own exit and keeps its meaning in the moved unit."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MinionProxy.swift"},"region":{"startLine":140}}}],"partialFingerprints":{"codehealthFindingId/v1":"17ef17b25889ba5f495dcf11ab42126021412d3e182feae6b6f51624e4be9917"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift:165-180 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetViewModel.swift:190-205 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":165}}}],"partialFingerprints":{"codehealthFindingId/v1":"b39f0cd201e0f9408d1e9ef135ae09f070d435edb4c4853b66901e8e31dcc396"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14\u201315 lines \u00D7 2): HSTracker/Hearthstone/Collection.swift:41-55 | HSTracker/Hearthstone/Collection.swift:167-180 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Collection.swift"},"region":{"startLine":41}}}],"partialFingerprints":{"codehealthFindingId/v1":"4c78714c18efde5ce7e89b5f90b37a47ddd1b14c8fb31e0b0614ad26dd392bbe"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2): HSTracker/Network/Http.swift:76-89 | HSTracker/Network/Http.swift:141-154 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Network/Http.swift:76\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Network/Http.swift"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"e62d27220028c84587dad322e816b6a8dfb669f7bd1f4a24ef48dfdfd3ad6da1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 4): HSTracker/HSReplay/HSReplayAPI.swift:776-788 | HSTracker/HSReplay/HSReplayAPI.swift:809-821 | HSTracker/HSReplay/HSReplayAPI.swift:904-916 | HSTracker/HSReplay/HSReplayAPI.swift:937-949 \u2014 all 4 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:776\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":776}}}],"partialFingerprints":{"codehealthFindingId/v1":"f27f136e4ef124c2a29c67de76112231547cdd12564e285f0f8ae03946714dad"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2): HSTracker/Core/RegionDrawer.swift:88-100 | HSTracker/Core/RegionDrawer.swift:130-142 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Each matched range is the entire body of the declaration above it, so the region is already a complete unit: move that whole declaration to the shared location and have each site call it, rather than lifting the lines out of their bodies. Any \u0060return\u0060 inside it is the body\u0027s own exit and keeps its meaning in the moved unit."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/RegionDrawer.swift"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"4097f3b2c8ff46d05bd9dc578ad4131beb9b95b557be53316b1cf15bed386b9b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2): HSTracker/HSReplay/HSReplayAPI.swift:564-576 | HSTracker/HSReplay/HSReplayAPI.swift:627-639 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:564\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":564}}}],"partialFingerprints":{"codehealthFindingId/v1":"0570599a6fa653fc8aac178db2dcca440ba5ed739c4f5b93b62fa67a739b92bc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11\u201313 lines \u00D7 2): HSTracker/HearthWatcher/PlayZoneWatcher.swift:24-34 | HSTracker/HearthWatcher/SpecialShopChoicesStateWatcher.swift:17-29 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HearthWatcher/PlayZoneWatcher.swift:24\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/PlayZoneWatcher.swift"},"region":{"startLine":24}}}],"partialFingerprints":{"codehealthFindingId/v1":"39b434881f3c81d53ed3f6c6065b91cebc1592dbb53eaf35e5ed77ae1506ee59"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11\u201312 lines \u00D7 3): HSTracker/Hearthstone/Secrets/SecretsManager.swift:306-316 | HSTracker/Hearthstone/Secrets/SecretsManager.swift:320-331 | HSTracker/Hearthstone/Secrets/SecretsManager.swift:436-446 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/Secrets/SecretsManager.swift:306\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/Hearthstone/Secrets/SecretsManager.swift:315\u0060 calls \u0060quantifiedCardsToCards\u0060, \u0060getFilteredSecretsByDrawer\u0060 and \u0060HSTracker/Hearthstone/Secrets/SecretsManager.swift:304\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":306}}}],"partialFingerprints":{"codehealthFindingId/v1":"ebdcdbd35b0db127983631aa9f57a97cd16b47a8dd7bfa86c5e948c3144b7cfc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 3): HSTracker/UIs/Battlegrounds/Guides/GuideCardArtBackground.swift:58-69 | HSTracker/UIs/Constructed/Mulligan/V2/MulliganCardPortraitView.swift:41-56 | HSTracker/UIs/Overlay/Mercenaries/MercenariesTaskView.swift:258-269 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/UIs/Overlay/Mercenaries/MercenariesTaskView.swift:254\u0060 calls \u0060clipShape\u0060, \u0060Ellipse\u0060, \u0060path\u0060 and \u0060HSTracker/UIs/Battlegrounds/Guides/GuideCardArtBackground.swift:54\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/GuideCardArtBackground.swift"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"23c0230c48ad8da8bef98a56d5261857dccb6488fed48b5f9e5ab66a228b1d5b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11\u201312 lines \u00D7 2): HSTracker/AppDelegate.swift:161-172 | HSTracker/AppDelegate.swift:182-192 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/AppDelegate.swift:161\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":161}}}],"partialFingerprints":{"codehealthFindingId/v1":"79134e751544f33ade2c660f4ca05e01fef19ab53a3c2c5eee229b4a36b2299e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9\u201312 lines \u00D7 2): HSTracker/BobsBuddy/BobsBuddyInvoker.swift:428-439 | HSTracker/Mono/MonoHelper.swift:597-605 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/BobsBuddy/BobsBuddyInvoker.swift:428\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also register a scope-exit action (a \u0060defer\u0060-style statement) that runs when the function holding them returns: moved into a called unit it would run when THAT unit returns instead \u2014 before the caller uses what it releases \u2014 so keep the registration at the call site and extract only the work around it, or have the extracted unit hand the resource back for the caller to register."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":428}}}],"partialFingerprints":{"codehealthFindingId/v1":"eba8bee6c582326b66b6fdfb7343fc5ca7f522e806cd91db84c01cc7b201cfa6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): HSTracker/Logging/Parsers/TagChangeActions.swift:675-686 | HSTracker/Logging/Parsers/TagChangeActions.swift:711-722 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Parsers/TagChangeActions.swift:675\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":675}}}],"partialFingerprints":{"codehealthFindingId/v1":"f40882d4244b16823357d660c8e2a73e9a61b816da5f5a2cdf256d37f8a04ef0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11\u201312 lines \u00D7 2): HSTracker/Mono/MonoHelper.swift:793-804 | HSTracker/Mono/MonoHelper.swift:821-831 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Mono/MonoHelper.swift:793\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":793}}}],"partialFingerprints":{"codehealthFindingId/v1":"0ee4a86fe71ff41e50d416f4af7875007ba8686adae32f9fe9c05d9e56e11491"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): HSTracker/Mono/MonoHelper.swift:861-872 | HSTracker/Mono/MonoHelper.swift:893-904 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Mono/MonoHelper.swift:861\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":861}}}],"partialFingerprints":{"codehealthFindingId/v1":"2aec99ecf60c0cdbd0441b94686199a79be29d94f25a2d2b420fbf7e5d6a8b43"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): HSTracker/UIs/DeckManager/DeckManager.swift:492-503 | HSTracker/UIs/DeckManager/DeckManager.swift:722-733 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/DeckManager/DeckManager.swift:492\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/DeckManager.swift"},"region":{"startLine":492}}}],"partialFingerprints":{"codehealthFindingId/v1":"e5addb418db7a5e952e68be8078180be168cc7b5e3f6d38fdb49625a37d391ca"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): HSTracker/UIs/Tooltips/RelatedCardsBrowserPanel.swift:271-282 | HSTracker/UIs/Tooltips/RelatedCardsTooltipPanel.swift:144-155 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Tooltips/RelatedCardsBrowserPanel.swift"},"region":{"startLine":271}}}],"partialFingerprints":{"codehealthFindingId/v1":"7a9611d5dd988266babb5fc5e8f171f87b83543087a0344c57303ea9ae8e0777"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 9): HSTracker/HSReplay/HSReplayAPI.swift:516-526 | HSTracker/HSReplay/HSReplayAPI.swift:579-589 | HSTracker/HSReplay/HSReplayAPI.swift:642-652 | HSTracker/HSReplay/HSReplayAPI.swift:758-768 | HSTracker/HSReplay/HSReplayAPI.swift:791-801 | HSTracker/HSReplay/HSReplayAPI.swift:886-896 | HSTracker/HSReplay/HSReplayAPI.swift:919-929 | HSTracker/HSReplay/HSReplayAPI.swift:952-962 | HSTracker/HSReplay/HSReplayAPI.swift:1380-1390 \u2014 all 9 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:516\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":516}}}],"partialFingerprints":{"codehealthFindingId/v1":"26f1c505ae5b868022100f01cd32cdafaf58fdbcf9b330efc46914c18f4ca79d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10\u201311 lines \u00D7 3): HSTracker/Logging/Game.swift:5134-5143 | HSTracker/Logging/Game.swift:5186-5196 | HSTracker/Logging/Game.swift:5235-5245 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5134}}}],"partialFingerprints":{"codehealthFindingId/v1":"0252325cba57929f5a685ee29077d2c861d5462d6de7e6f4a76f7f49b4a6752f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 3): HSTracker/UIs/Arena/ArenaPreDraftView.swift:271-281 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:220-230 | HSTracker/UIs/Constructed/Mulligan/MulliganGuideTrialsExhaustedView.swift:61-71 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":271}}}],"partialFingerprints":{"codehealthFindingId/v1":"5140342f96d5eed3c1c08e090dec17e8bc6a662e7101a46fe9367a37183e5c05"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10\u201311 lines \u00D7 2): HSTracker/HSReplay/HSReplayAPI.swift:249-259 | HSTracker/HSReplay/HSReplayAPI.swift:295-304 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:249\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note that the copies do not run to the end of the range shown: their LAST lines are different code, not the same code under different names \u2014 the matched region ends inside that line. Extract the lines above it, and read the last line of each site separately."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":249}}}],"partialFingerprints":{"codehealthFindingId/v1":"3467e18d6224e889f7b4822e319ac21ce54a6b4fa0d97df2ce134a4015c78360"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/HSReplay/HSReplayAPI.swift:531-541 | HSTracker/HSReplay/HSReplayAPI.swift:594-604 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:531\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":531}}}],"partialFingerprints":{"codehealthFindingId/v1":"ac4d64b203c816dd380a0e5d8d347a4ea7bbd95b1f79b78ba052ad14848513e8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/Mono/MonoHelper.swift:839-849 | HSTracker/Mono/MonoHelper.swift:908-918 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Mono/MonoHelper.swift:839\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":839}}}],"partialFingerprints":{"codehealthFindingId/v1":"8c7a96590f6997a3a5ae04c3172d77bc750e8847ca7336c81ab5eeaf91f576c0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/Mono/ReplicatingMenace.swift:26-36 | HSTracker/Mono/WhirringProtector.swift:26-36 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Mono/ReplicatingMenace.swift:26\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/ReplicatingMenace.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"dbaef21936e15a0c19fda262a747f227f7cc70537acf53fbb3969334d6eb938a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPreDraftView.swift:74-84 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:85-95 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:74\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":74}}}],"partialFingerprints":{"codehealthFindingId/v1":"193078cb3e2f790531a0842e748da695915c330d38c0c5dc12d277e3f6786601"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Arena/ArenaVectorIcons.swift:41-51 | HSTracker/UIs/Arena/ArenaVectorIcons.swift:66-76 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaVectorIcons.swift"},"region":{"startLine":41}}}],"partialFingerprints":{"codehealthFindingId/v1":"7a98410dc2d8ce2c41baf3b47fde4a7a90de944caca1726dea3056e7d905f582"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift:363-373 | HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift:110-120 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift"},"region":{"startLine":363}}}],"partialFingerprints":{"codehealthFindingId/v1":"7262ca059bc3012a2518d841d802259cdc6fd65dbe841c134854fb77a8454711"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift:153-163 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetViewModel.swift:174-184 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetViewModel.swift:186\u0060 calls \u0060signIn\u0060, \u0060instance\u0060, \u0060openPreferences\u0060 and \u0060HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift:165\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":153}}}],"partialFingerprints":{"codehealthFindingId/v1":"cf53e90ed9f3b6e096ac15adb1fbc487851940f2c5ba6fd7bea681b10d4f679b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8\u201310 lines \u00D7 3): HSTracker/UIs/Arena/ArenaPreDraftView.swift:296-304 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:121-128 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:151-160 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:296\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note first that the copies are not typed on the same thing: the declarations holding them bind \u0060alignment\u0060 to \u0060.topTrailing\u0060 in one and \u0060.leading\u0060 in another, and the duplicated lines use it. The extracted unit therefore needs a parameter type that fits BOTH \u2014 their common supertype where they have one, or a new abstraction over them where they do not \u2014 and settling that is the step that comes BEFORE the extraction above. Where the two types are deliberately unrelated, the duplication is the price of that separation and the honest resolution is to record the decision rather than to extract."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":296}}}],"partialFingerprints":{"codehealthFindingId/v1":"c283f41fb3e145d8e830439c1b6b9c15ea42861066dda05c00116dc1154f5aee"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/BgCounters/FreeRefreshCounter.swift:49-58 | HSTracker/Hearthstone/CounterSystem/BgCounters/NextRefreshDemonFodderCounter.swift:48-57 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/BgCounters/FreeRefreshCounter.swift:49\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/FreeRefreshCounter.swift"},"region":{"startLine":49}}}],"partialFingerprints":{"codehealthFindingId/v1":"0df41e348c0fd9e963cb3714a32ac93596618c6e467e2ac38122c71f6997fc1f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/BeastsSummonedThisGameCounter.swift:49-58 | HSTracker/Hearthstone/CounterSystem/Counters/PlayedDragonsCounter.swift:48-57 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/BeastsSummonedThisGameCounter.swift:49\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/BeastsSummonedThisGameCounter.swift"},"region":{"startLine":49}}}],"partialFingerprints":{"codehealthFindingId/v1":"33f104f9be5951b789cb262d3915cdac44ec87e21c9d079cf52a77e3e11caa4d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/HolySpellsPlayedCounter.swift:49-58 | HSTracker/Hearthstone/CounterSystem/Counters/SpellsPlayedInCharactersCounter.swift:49-58 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/HolySpellsPlayedCounter.swift:49\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/HolySpellsPlayedCounter.swift"},"region":{"startLine":49}}}],"partialFingerprints":{"codehealthFindingId/v1":"77898446f19f2de81a61fcc855d49d473ebde32bf1442e02b8c022bcb369502f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift:237-246 | HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift:322-331 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift:237\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/RelatedCardsManager.swift"},"region":{"startLine":237}}}],"partialFingerprints":{"codehealthFindingId/v1":"95a76375eee2cefd3b5b1bdbe8ffa0ced94b9069b4ebb2cf45ecc00313befb63"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/Mono/MonoHelper.swift:941-950 | HSTracker/Mono/MonoHelper.swift:967-976 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":941}}}],"partialFingerprints":{"codehealthFindingId/v1":"0089090bdc4d2e57683bfe9e1f05090a4b8d5a836fb2584cdbee9367690c9e93"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:199-208 | HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionView.swift:424-433 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift"},"region":{"startLine":199}}}],"partialFingerprints":{"codehealthFindingId/v1":"7c27fd16b059c05f075d23912c1615475bb9c3ef98ea1d45659c777a345c2b20"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/UIs/Preferences/CountersPreferences.swift:81-90 | HSTracker/UIs/Preferences/RelatedCardsPreferences.swift:67-76 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Preferences/CountersPreferences.swift:81\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/UIs/Preferences/RelatedCardsPreferences.swift:77\u0060 calls \u0060Toggle\u0060, \u0060localizedString\u0060 and \u0060HSTracker/UIs/Preferences/CountersPreferences.swift:91\u0060 does not \u2014 after which the two agree again for 3 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/CountersPreferences.swift"},"region":{"startLine":81}}}],"partialFingerprints":{"codehealthFindingId/v1":"7da5e79d5b55b774fba3b28d24d22def9d14534ecf7901ada5389151fad0b9f3"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): HSTracker/UIs/Arena/ArenaPreDraftView.swift:52-60 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:62-70 | HSTracker/UIs/Constructed/Mulligan/MulliganGuideTrialsExhaustedView.swift:28-36 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:52\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note first that the copies are not typed on the same thing: the declarations holding them bind \u0060spacing\u0060 to \u00604\u0060 in one and \u00608\u0060 in another, and the duplicated lines use it. The extracted unit therefore needs a parameter type that fits BOTH \u2014 their common supertype where they have one, or a new abstraction over them where they do not \u2014 and settling that is the step that comes BEFORE the extraction above. Where the two types are deliberately unrelated, the duplication is the price of that separation and the honest resolution is to record the decision rather than to extract."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":52}}}],"partialFingerprints":{"codehealthFindingId/v1":"4bc0e6dc1fc3dd75da0ae114eb302e33505bad3f4c745277dcbd2224b64f5dc6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift:364-372 | HSTracker/UIs/Battlegrounds/Guides/GuideTooltipCardView.swift:63-71 | HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift:111-119 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times. Each matched range is the entire body of the declaration above it, so the region is already a complete unit: move that whole declaration to the shared location and have each site call it, rather than lifting the lines out of their bodies. Any \u0060return\u0060 inside it is the body\u0027s own exit and keeps its meaning in the moved unit."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift"},"region":{"startLine":364}}}],"partialFingerprints":{"codehealthFindingId/v1":"604bcfe244ea1a350702b898093ea4e0fe542d3af4627d035697369538eae04c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2307-2315 | HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2381-2389 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2307\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":2307}}}],"partialFingerprints":{"codehealthFindingId/v1":"48f72d9063257900a48629fec0dc2e15fce0df5866e2dea61126844dae5df016"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/Core/Extensions/Color.swift:16-24 | HSTracker/Core/Extensions/Color.swift:49-57 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Note first that the copies are not typed on the same thing: the declarations holding them bind \u0060hex\u0060 to \u0060String) -\u003E (red: CGFloat\u0060 in one and \u0060String\u0060 in another, and the duplicated lines use it. The extracted unit therefore needs a parameter type that fits BOTH \u2014 their common supertype where they have one, or a new abstraction over them where they do not \u2014 and settling that is the step that comes BEFORE the extraction above. Where the two types are deliberately unrelated, the duplication is the price of that separation and the honest resolution is to record the decision rather than to extract."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/Extensions/Color.swift"},"region":{"startLine":16}}}],"partialFingerprints":{"codehealthFindingId/v1":"7278ae33e4036dfeefbb52e0a077a247f363bcadbd5f3ddb2c25cc9dc9e58513"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/LeylineCostReductionCounter.swift:38-46 | HSTracker/Hearthstone/CounterSystem/Counters/SilverHandRecruitBuffCounter.swift:47-55 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/LeylineCostReductionCounter.swift:38\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/LeylineCostReductionCounter.swift"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"270581bca4473b611027d37a302779e0690b21e1e133eaf29e7377469b88cd82"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/Logging/Parsers/TagChangeActions.swift:1375-1383 | HSTracker/Logging/Parsers/TagChangeActions.swift:1482-1490 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Parsers/TagChangeActions.swift:1375\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1375}}}],"partialFingerprints":{"codehealthFindingId/v1":"9c9ee76721e5934b9dfd4aec687148ede83fc05e2c97525d1347f0193d32d988"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/Mono/MonoHelper.swift:1016-1024 | HSTracker/Mono/MonoHelper.swift:1036-1044 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/Mono/MonoHelper.swift:1046\u0060 calls \u0060UnsafeMutableRawPointer\u0060, \u0060get\u0060 and \u0060HSTracker/Mono/MonoHelper.swift:1027\u0060 does not \u2014 after which the two agree again for 3 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":1016}}}],"partialFingerprints":{"codehealthFindingId/v1":"8f5d138c9d1d003abb0d8a855352af5d4f60b8671fa83299c901b9817912d1ee"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8\u20139 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPreDraftView.swift:101-109 | HSTracker/UIs/Arena/ArenaPreDraftView.swift:196-203 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:101\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note that the copies do not run to the end of the range shown: their LAST lines are different code, not the same code under different names \u2014 the matched region ends inside that line. Extract the lines above it, and read the last line of each site separately."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":101}}}],"partialFingerprints":{"codehealthFindingId/v1":"b54fbfab4c5aab7bb1f0631579f7239e4cbb8b9c8cf8c6b10c8af08d8af7d379"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (3\u20139 lines \u00D7 3): HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningShopView.swift:171-179 | HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:156-158 | HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:595-603 \u2014 there are 3 copies across 2 file(s) \u2014 more copies than files, so at least one file holds the block twice. Extract it once into a single shared function every call site can reach and call it from all 3 sites; resolving a subset leaves the remainder to drift apart. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningShopView.swift:171\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningShopView.swift"},"region":{"startLine":171}}}],"partialFingerprints":{"codehealthFindingId/v1":"456265ebeccd3ba855c4f234c9c4d944d069870da0e722c1848ba24b9a5ef21b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift:127-135 | HSTracker/UIs/Overlay/Trackers/TrackerPanelViewModel.swift:275-283 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Each matched range is the entire body of the declaration above it, so the region is already a complete unit: move that whole declaration to the shared location and have each site call it, rather than lifting the lines out of their bodies. Any \u0060return\u0060 inside it is the body\u0027s own exit and keeps its meaning in the moved unit."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift"},"region":{"startLine":127}}}],"partialFingerprints":{"codehealthFindingId/v1":"5466d8aed0e0db54f5fe145c592e03a7524affd35368db34b4975522fd2276e4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/UIs/Preferences/CountersPreferences.swift:181-189 | HSTracker/UIs/Preferences/RelatedCardsPreferences.swift:138-146 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Preferences/CountersPreferences.swift:181\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/CountersPreferences.swift"},"region":{"startLine":181}}}],"partialFingerprints":{"codehealthFindingId/v1":"3ccca3dea458a46d903560b43a9e22232ba7058f6c760e28af08671284545df4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/UIs/Preferences/GamePreferences.swift:146-154 | HSTracker/UIs/Preferences/InitialConfiguration.swift:141-149 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Preferences/GamePreferences.swift\u0060 and \u0060HSTracker/UIs/Preferences/InitialConfiguration.swift\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 33 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. If that is what happened, the fix is to keep one copy and have the other call it (or delete it), which resolves this row and its siblings together \u2014 extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/GamePreferences.swift"},"region":{"startLine":146}}}],"partialFingerprints":{"codehealthFindingId/v1":"712478e1acca9dc3a7879070e3e5a99af317a0b3290376d187cbec60f50461f1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 4): HSTracker/Database/RealmHelper.swift:469-476 | HSTracker/Database/RealmHelper.swift:489-496 | HSTracker/Database/RealmHelper.swift:513-520 | HSTracker/Database/RealmHelper.swift:545-552 \u2014 all 4 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Database/RealmHelper.swift:469\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/RealmHelper.swift"},"region":{"startLine":469}}}],"partialFingerprints":{"codehealthFindingId/v1":"e9706cf8d44ff7affb2e334e65a16095466d5cdf1a924280052e55990b67bd69"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6\u20138 lines \u00D7 4): HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift:65-71 | HSTracker/Hearthstone/CounterSystem/Counters/PaladinCardsPlayedCounter.swift:44-51 | HSTracker/Hearthstone/CounterSystem/Counters/PlayedSpellSchoolsCounter.swift:73-78 | HSTracker/Hearthstone/CounterSystem/Counters/SpellsPlayedInCharactersCounter.swift:45-52 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 4 call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift:65\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift:61\u0060 calls \u0060String\u0060, \u0060localizedString\u0060 and \u0060HSTracker/Hearthstone/CounterSystem/Counters/PaladinCardsPlayedCounter.swift:39\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"30573f9c59ddccedd9b1afdf55043a8a93cd5f37299781e882b7fea5d6bb61ec"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): HSTracker/Mono/EnchantmentFactoryProxy.swift:32-39 | HSTracker/Mono/MinionFactoryProxy.swift:44-51 | HSTracker/Mono/ObjectiveFactoryProxy.swift:32-39 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 3 call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/EnchantmentFactoryProxy.swift"},"region":{"startLine":32}}}],"partialFingerprints":{"codehealthFindingId/v1":"d714c044db6424dd112d2f93729b0278c78cfcc9a91259d1b89b07d714203d22"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:94-101 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift:50-57 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganSingleCardHeaderView.swift:130-137 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift\u0060 and \u0060HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift\u0060 as WHOLE FILES: this scan already matched 5 separate duplicated blocks between them, totalling at least 46 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:94\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift"},"region":{"startLine":94}}}],"partialFingerprints":{"codehealthFindingId/v1":"29c0031f155de59ae1fe09bde231aab3a367dc87af087bc63d9af8931ba005ae"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6\u20138 lines \u00D7 3): HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift:126-133 | HSTracker/UIs/Overlay/Trackers/SecretsPanelViewModel.swift:61-66 | HSTracker/UIs/Overlay/Trackers/TrackerPanelViewModel.swift:274-281 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift:126\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note that the copies do not run to the end of the range shown: their LAST lines are different code, not the same code under different names \u2014 the matched region ends inside that line. Extract the lines above it, and read the last line of each site separately."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift"},"region":{"startLine":126}}}],"partialFingerprints":{"codehealthFindingId/v1":"7e70e7503a7d19cac20c73326a47b4aa0e27cdb20af839e9dab4edd9251f1a34"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2131-2138 | HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2169-2176 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2131\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":2131}}}],"partialFingerprints":{"codehealthFindingId/v1":"60b47dd98224e89ec83cfaca625d952e0f7714f11cd37d63d2cc454cea72bacd"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/Logging/Player.swift:371-378 | HSTracker/Logging/Player.swift:593-600 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Player.swift:371\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":371}}}],"partialFingerprints":{"codehealthFindingId/v1":"339290ddd6f31df37ed8ebd6a2e1bab5b2934aa5f0f05ad7f9b02dc5350f084c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20138 lines \u00D7 2): HSTracker/Logging/Player.swift:409-415 | HSTracker/Logging/Player.swift:769-776 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Player.swift:409\u0060 it runs out through the closing brace of the declaration holding it and carries on into the declaration that follows \u2014 the window is the tail of one member plus the head of the next, so no call can be substituted for those exact lines, and the smallest declaration that contains all of them is the type they sit in. The repeated unit is the member each site sits in: where those members\u0027 bodies are the same, move one whole member to the shared location and have the others delegate to it; where the copies are a run of near-identical overloads or wrappers that differ only in their signatures, the repetition IS the run \u2014 a one-line delegation has no helper inside it to lift \u2014 so generate the run from the set it enumerates, or accept it and keep each member\u0027s own documentation with it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":409}}}],"partialFingerprints":{"codehealthFindingId/v1":"4e99f5b9ae9aeb8fbc98525a4d7df5f5e6acb6f6e2bb41a68576bffacdafc560"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/Logging/Player.swift:1075-1082 | HSTracker/Logging/Player.swift:1085-1092 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Each matched range is the entire body of the declaration above it, so the region is already a complete unit: move that whole declaration to the shared location and have each site call it, rather than lifting the lines out of their bodies. Any \u0060return\u0060 inside it is the body\u0027s own exit and keeps its meaning in the moved unit."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":1075}}}],"partialFingerprints":{"codehealthFindingId/v1":"219fe204b82f881856c503862fa697e8d76edb7f7934ff4257b71f1a3c317f8b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20138 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPreDraftView.swift:170-177 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:202-208 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 95 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:170\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:169\u0060 calls \u0060fixedSize\u0060 and \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:201\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":170}}}],"partialFingerprints":{"codehealthFindingId/v1":"02b4a8e4588900052d472fd2f53035db3ab759ad04c895026e80f21bb4d98698"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20138 lines \u00D7 2): HSTracker/UIs/Battlegrounds/BobsBuddy/BobsBuddyPanelView.swift:159-166 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:136-142 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/BobsBuddy/BobsBuddyPanelView.swift:159\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note that the copies do not run to the end of the range shown: their LAST lines are different code, not the same code under different names \u2014 the matched region ends inside that line. Extract the lines above it, and read the last line of each site separately."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BobsBuddy/BobsBuddyPanelView.swift"},"region":{"startLine":159}}}],"partialFingerprints":{"codehealthFindingId/v1":"985e8e658f3d3fdb22c091fd18abce4b18123f6084efb2bcb55616ddfcc596db"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:571-578 | HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsView.swift:274-281 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:571\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The \u0060return\u0060 at the foot of the matched lines is the enclosing body\u0027s own terminal exit, not an early one: it moves with them unchanged, and each site calls the extracted unit from the position that \u0060return\u0060 occupied \u2014 no decision has to be handed back and re-acted on."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift"},"region":{"startLine":571}}}],"partialFingerprints":{"codehealthFindingId/v1":"e710566df830abbe64ff7bc862c36a249bea69f01ddb5fb1d6cc312b1c64e106"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20138 lines \u00D7 2): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:44-51 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift:77-83 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift\u0060 and \u0060HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift\u0060 as WHOLE FILES: this scan already matched 5 separate duplicated blocks between them, totalling at least 46 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:44\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift"},"region":{"startLine":44}}}],"partialFingerprints":{"codehealthFindingId/v1":"6436356390d16dc8c5738ead0d8a3e0c6364c2a720fccc7fbd158b1a5d29ef9b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/UIs/Overlay/Trackers/SecretsPanelViewModel.swift:72-79 | HSTracker/UIs/Overlay/Trackers/TrackerPanelViewModel.swift:288-295 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Trackers/SecretsPanelViewModel.swift"},"region":{"startLine":72}}}],"partialFingerprints":{"codehealthFindingId/v1":"a2579a1c618ab1db0d671535397529951589be2214b2079e564ed5cb6d640cac"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/UIs/Preferences/GamePreferences.swift:56-63 | HSTracker/UIs/Preferences/InitialConfiguration.swift:35-42 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Preferences/GamePreferences.swift\u0060 and \u0060HSTracker/UIs/Preferences/InitialConfiguration.swift\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 33 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. If that is what happened, the fix is to keep one copy and have the other call it (or delete it), which resolves this row and its siblings together \u2014 extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/GamePreferences.swift"},"region":{"startLine":56}}}],"partialFingerprints":{"codehealthFindingId/v1":"dc248e390a83aa6b06c74cde79e12b65369dbd653e09e15cf1da90299b6ca0b7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/Utility/Arena/ArenaLastDrafts.swift:186-193 | HSTracker/Utility/Arena/ArenaLastDrafts.swift:280-287 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/Utility/Arena/ArenaLastDrafts.swift:196\u0060 calls \u0060compactMap\u0060, \u0060CardPackage\u0060 and \u0060HSTracker/Utility/Arena/ArenaLastDrafts.swift:289\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/Arena/ArenaLastDrafts.swift"},"region":{"startLine":186}}}],"partialFingerprints":{"codehealthFindingId/v1":"e5e74f6015477850f0ba8403e694f2781c4e2e1097b65397ae45ff5e23a32a84"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 4): HSTracker/Hearthstone/CounterSystem/BgCounters/BloodGemBarrageBuffCounter.swift:44-50 | HSTracker/Hearthstone/CounterSystem/BgCounters/RandomTavernMinionBuffCounter.swift:49-56 | HSTracker/Hearthstone/CounterSystem/BgCounters/VolumizerBuffCounter.swift:50-57 | HSTracker/Hearthstone/CounterSystem/BgCounters/WhelpStatsBuffCounter.swift:47-54 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 4 call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/BgCounters/RandomTavernMinionBuffCounter.swift:49\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/BloodGemBarrageBuffCounter.swift"},"region":{"startLine":44}}}],"partialFingerprints":{"codehealthFindingId/v1":"3998333001f5b8ce21d15f0127764a2cb5647cc0821244689ad0c819c4b0f9b4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 3): HSTracker/Logging/Player.swift:967-973 | HSTracker/Logging/Player.swift:1075-1081 | HSTracker/Logging/Player.swift:1085-1091 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":967}}}],"partialFingerprints":{"codehealthFindingId/v1":"962e1befce72709f5da405192635594d4f83252256eead175e8b45b4e88c02d8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 3): HSTracker/UIs/Arena/ArenaPreDraftView.swift:102-108 | HSTracker/UIs/Arena/ArenaPreDraftView.swift:197-203 | HSTracker/UIs/Arena/ArenaPreDraftView.swift:246-252 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPreDraftView.swift:102\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":102}}}],"partialFingerprints":{"codehealthFindingId/v1":"00bc9db0ba6c8e941af55e2709713420d216c21f35b6232f4c17fbaa871cc1e3"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2050-2056 | HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2077-2083 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/BobsBuddy/BobsBuddyInvoker.swift:2050\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":2050}}}],"partialFingerprints":{"codehealthFindingId/v1":"51f2bf8a640505456e8fbfde4302c686597c2ba0ee799cbe4f8d703b30c0e1cf"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/HearthWatcher/ArenaWatcher.swift:186-192 | HSTracker/HearthWatcher/ArenaWatcher.swift:199-205 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HearthWatcher/ArenaWatcher.swift:186\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/ArenaWatcher.swift"},"region":{"startLine":186}}}],"partialFingerprints":{"codehealthFindingId/v1":"82e95592f969b8b68f537b477d757a4f83cf514bfeccd02bfcd3237f507e5e3b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/BgCounters/BeetlesSizeCounter.swift:56-62 | HSTracker/Hearthstone/CounterSystem/BgCounters/SpellsPlayedForNagasCounter.swift:48-54 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/BeetlesSizeCounter.swift"},"region":{"startLine":56}}}],"partialFingerprints":{"codehealthFindingId/v1":"ad984d8464a111117cdf070bf9fd3fec70021640a103686c34706d5223f262e5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/FriendlyMinionsDiedThisGameCounter.swift:63-69 | HSTracker/Hearthstone/CounterSystem/Counters/MinionsDiedThisGameCounter.swift:47-53 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/FriendlyMinionsDiedThisGameCounter.swift:63\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/FriendlyMinionsDiedThisGameCounter.swift"},"region":{"startLine":63}}}],"partialFingerprints":{"codehealthFindingId/v1":"85251a3a5071e123761bce0eaee045d761bcce3d0307ebbcfa8bdd640df361f4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/TyrsTears.swift:21-27 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/TyrsTears.swift:43-49 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/TyrsTears.swift"},"region":{"startLine":21}}}],"partialFingerprints":{"codehealthFindingId/v1":"e88bdcdfe807554df51d721e6ed4e7081eb2bc3abade051d05ccdab79e22bf01"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/Watchers.swift:167-173 | HSTracker/Hearthstone/Watchers.swift:230-236 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/Watchers.swift:167\u0060 it runs out through the closing brace of the declaration holding it and carries on into the declaration that follows \u2014 the window is the tail of one member plus the head of the next, so no call can be substituted for those exact lines, and the smallest declaration that contains all of them is the type they sit in. The repeated unit is the member each site sits in: where those members\u0027 bodies are the same, move one whole member to the shared location and have the others delegate to it; where the copies are a run of near-identical overloads or wrappers that differ only in their signatures, the repetition IS the run \u2014 a one-line delegation has no helper inside it to lift \u2014 so generate the run from the set it enumerates, or accept it and keep each member\u0027s own documentation with it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Watchers.swift"},"region":{"startLine":167}}}],"partialFingerprints":{"codehealthFindingId/v1":"dcd8cf1ddcced0594eff405841475d30aa3b003df12072f5bd9dae5c724d88b1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Logging/Player.swift:633-639 | HSTracker/Logging/Player.swift:767-773 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Player.swift:633\u0060 it runs out through the closing brace of the declaration holding it and carries on into the declaration that follows \u2014 the window is the tail of one member plus the head of the next, so no call can be substituted for those exact lines, and the smallest declaration that contains all of them is the type they sit in. The repeated unit is the member each site sits in: where those members\u0027 bodies are the same, move one whole member to the shared location and have the others delegate to it; where the copies are a run of near-identical overloads or wrappers that differ only in their signatures, the repetition IS the run \u2014 a one-line delegation has no helper inside it to lift \u2014 so generate the run from the set it enumerates, or accept it and keep each member\u0027s own documentation with it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":633}}}],"partialFingerprints":{"codehealthFindingId/v1":"ebc5c602911929179bb97d000ca98419f8448165737061ac5fa51e3295a3906e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift:71-77 | HSTracker/UIs/Battlegrounds/Guides/Quests/QuestGuideView.swift:50-56 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift:71\u0060 it runs out through the closing brace of the declaration holding it and carries on into the declaration that follows \u2014 the window is the tail of one member plus the head of the next, so no call can be substituted for those exact lines, and the smallest declaration that contains all of them is the type they sit in. The repeated unit is the member each site sits in: where those members\u0027 bodies are the same, move one whole member to the shared location and have the others delegate to it; where the copies are a run of near-identical overloads or wrappers that differ only in their signatures, the repetition IS the run \u2014 a one-line delegation has no helper inside it to lift \u2014 so generate the run from the set it enumerates, or accept it and keep each member\u0027s own documentation with it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Heroes/HeroGuideView.swift"},"region":{"startLine":71}}}],"partialFingerprints":{"codehealthFindingId/v1":"0575fe1e97f10ae5354b7a32aaee3dce2b08ca7fd0d3d712f6940c28c9e58aba"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:249-255 | HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:270-276 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:249\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift"},"region":{"startLine":249}}}],"partialFingerprints":{"codehealthFindingId/v1":"911b2708f2ca5efe7b1d934ff406e5fbbb1d1e53189f5e6fa77d55e7aefbae81"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:215-221 | HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:513-519 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:215\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:510\u0060 calls \u0060VStack\u0060, \u0060popupHeader\u0060, \u0060localizedString\u0060 and \u0060HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:214\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift"},"region":{"startLine":215}}}],"partialFingerprints":{"codehealthFindingId/v1":"33ffdd36c017433cca6c9a762585fb9b323d8542c72bc9eb9f3cbe3d6a084253"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:227-233 | HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:245-253 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:245\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift"},"region":{"startLine":227}}}],"partialFingerprints":{"codehealthFindingId/v1":"5a2b30408769d0a0f5c46ad3ed3b9a390108eab23a5040d278eba3a1a1c4c60c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/DeckManager/EditDeck.swift:321-327 | HSTracker/UIs/DeckManager/EditDeck.swift:346-352 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/DeckManager/EditDeck.swift:321\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/EditDeck.swift"},"region":{"startLine":321}}}],"partialFingerprints":{"codehealthFindingId/v1":"89b8b0299800ec2ea32eb9de1ab32b0b3c4c0703543281d4ce351177d1c799c2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Preferences/GamePreferences.swift:116-122 | HSTracker/UIs/Preferences/InitialConfiguration.swift:121-127 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Preferences/GamePreferences.swift\u0060 and \u0060HSTracker/UIs/Preferences/InitialConfiguration.swift\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 33 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. If that is what happened, the fix is to keep one copy and have the other call it (or delete it), which resolves this row and its siblings together \u2014 extracting one helper per block leaves the fork in place. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Preferences/GamePreferences.swift:116\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/GamePreferences.swift"},"region":{"startLine":116}}}],"partialFingerprints":{"codehealthFindingId/v1":"075fbdbbe28bc4c3dd6978f13bb849d3cc3aab561a2e7cf343a1c20ef898180c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 6): HSTracker/HSReplay/HSReplayAPI.swift:563-568 | HSTracker/HSReplay/HSReplayAPI.swift:626-631 | HSTracker/HSReplay/HSReplayAPI.swift:688-693 | HSTracker/HSReplay/HSReplayAPI.swift:846-851 | HSTracker/HSReplay/HSReplayAPI.swift:999-1004 | HSTracker/HSReplay/HSReplayAPI.swift:1427-1432 \u2014 all 6 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:563\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":563}}}],"partialFingerprints":{"codehealthFindingId/v1":"2c6cca8284f8da66fef3c0664cbbb4942b58dab9dd9d75ac61617dad99e5036a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5\u20136 lines \u00D7 3): HSTracker/Logging/Game.swift:5145-5149 | HSTracker/Logging/Game.swift:5198-5203 | HSTracker/Logging/Game.swift:5247-5252 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Game.swift:5198\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note that the copies do not run to the end of the range shown: their LAST lines are different code, not the same code under different names \u2014 the matched region ends inside that line. Extract the lines above it, and read the last line of each site separately."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5145}}}],"partialFingerprints":{"codehealthFindingId/v1":"0e7a887fc4aa4edf19c0cc369b5f664d2409f61708ff713cb285ceb76b1e30d6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/AppDelegate.swift:626-631 | HSTracker/AppDelegate.swift:634-639 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/AppDelegate.swift:626\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":626}}}],"partialFingerprints":{"codehealthFindingId/v1":"3cff172dda94d91f31ea62e4ad14f0a0fb7da6282800bb5edc4889ffc3c8898c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/AppDelegate.swift:844-849 | HSTracker/UIs/Preferences/BattlegroundsPreferences.swift:293-298 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":844}}}],"partialFingerprints":{"codehealthFindingId/v1":"753c7b56fde4397f5e08c707f8ec8a01d56f72e4a2600c5658d9d8876076777a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/HSReplay/HSReplayNetHelper.swift:43-48 | HSTracker/HSReplay/HSReplayNetHelper.swift:96-101 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayNetHelper.swift:43\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayNetHelper.swift"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"40603c95d2d24b5c83c0e323260d2948bfe6751e9170259e30a16f409b91856b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/Logging/Game.swift:5121-5126 | HSTracker/Logging/Game.swift:5171-5176 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5121}}}],"partialFingerprints":{"codehealthFindingId/v1":"564c254e7df91b0fa8a066e860bdfdff98247fc48bcac2d7954277a6ac071748"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/Logging/Parsers/PowerGameStateParser.swift:352-357 | HSTracker/Logging/Parsers/PowerGameStateParser.swift:376-381 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Parsers/PowerGameStateParser.swift:352\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":352}}}],"partialFingerprints":{"codehealthFindingId/v1":"56fc3fc3cc72d11d50fbbfb2580baa1d95abd19bbeb770b97b20d5f43350b52a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/Statistics/LadderGrid.swift:50-55 | HSTracker/Statistics/LadderGrid.swift:58-63 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Statistics/LadderGrid.swift"},"region":{"startLine":50}}}],"partialFingerprints":{"codehealthFindingId/v1":"06808ea74bc2284fe25324202cc4179dbc07d438a717106a879c8f557102c09b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift:194-199 | HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningShopView.swift:193-198 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift:194\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift"},"region":{"startLine":194}}}],"partialFingerprints":{"codehealthFindingId/v1":"829b02ada7f7b8e1e8e3213d9897f97a5647e5d872cf2f211b844ab2fc76faf0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/Utility/Arena/ArenaLastDrafts.swift:44-52 | HSTracker/Utility/Arena/ArenaLastDrafts.swift:69-74 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/Arena/ArenaLastDrafts.swift"},"region":{"startLine":44}}}],"partialFingerprints":{"codehealthFindingId/v1":"4f79abdcebd445b8ac9fe7266e5973829e11d3f3027975a5b6ebdcbf77456427"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 7): HSTracker/HSReplay/HSReplayAPI.swift:563-567 | HSTracker/HSReplay/HSReplayAPI.swift:626-630 | HSTracker/HSReplay/HSReplayAPI.swift:688-692 | HSTracker/HSReplay/HSReplayAPI.swift:846-850 | HSTracker/HSReplay/HSReplayAPI.swift:999-1003 | HSTracker/HSReplay/HSReplayAPI.swift:1060-1064 | HSTracker/HSReplay/HSReplayAPI.swift:1427-1431 \u2014 all 7 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/HSReplay/HSReplayAPI.swift:563\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/HSReplay/HSReplayAPI.swift:568\u0060 calls \u0060debug\u0060 and \u0060HSTracker/HSReplay/HSReplayAPI.swift:1065\u0060 does not \u2014 after which the two agree again for 4 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":563}}}],"partialFingerprints":{"codehealthFindingId/v1":"87a0585bfe540e3be8f110ffb522fdbb12aaedb0a3109f72d83238888015e766"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 5): HSTracker/Hearthstone/CounterSystem/BgCounters/AncestralAutomatonCounter.swift:63-67 | HSTracker/Hearthstone/CounterSystem/BgCounters/BeetlesSizeCounter.swift:54-58 | HSTracker/Hearthstone/CounterSystem/BgCounters/EternalKnightCounter.swift:57-61 | HSTracker/Hearthstone/CounterSystem/BgCounters/SpellsPlayedForNagasCounter.swift:46-50 | HSTracker/Hearthstone/CounterSystem/BgCounters/UndeadBuffCounter.swift:43-47 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 5 call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/BgCounters/AncestralAutomatonCounter.swift:63\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/AncestralAutomatonCounter.swift"},"region":{"startLine":63}}}],"partialFingerprints":{"codehealthFindingId/v1":"d08c89ded828863d745a506a4280000ab9149701807ace8011e1e616922a8a63"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 5): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:33-37 | HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:68-72 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:43-47 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:100-104 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:125-129 \u2014 there are 5 copies across 2 file(s) \u2014 more copies than files, so at least one file holds the block twice. Extract it once into a single shared function every call site can reach and call it from all 5 sites; resolving a subset leaves the remainder to drift apart. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:33\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:39\u0060 calls \u0060addLine\u0060, \u0060p\u0060 and \u0060HSTracker/UIs/Overlay/HDTCanvasIcon.swift:49\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift"},"region":{"startLine":33}}}],"partialFingerprints":{"codehealthFindingId/v1":"6297ba4bfa8f87f6fa1ba9b5247fa6f9f01e98d6cd30be095f57768170ba1fd1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 4): HSTracker/Mono/Leapfrogger.swift:26-30 | HSTracker/Mono/ReplicatingMenace.swift:26-30 | HSTracker/Mono/Scallywag.swift:26-30 | HSTracker/Mono/WhirringProtector.swift:26-30 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 4 call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Mono/Leapfrogger.swift:26\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/Leapfrogger.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"e3b2f6620a5183113290d0ebb9f6593a6250c7996138b2d5ae4d30f947e4f19b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): HSTracker/Network/Http.swift:84-88 | HSTracker/Network/Http.swift:149-153 | HSTracker/Network/Http.swift:175-180 \u2014 all 3 copies are in the same file, so extract the block into one function there and call it from every one of those sites \u2014 resolving only two of them leaves the rest to drift apart the first time one is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Network/Http.swift:84\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/Network/Http.swift:81\u0060 calls \u0060fulfill\u0060 and \u0060HSTracker/Network/Http.swift:171\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Network/Http.swift"},"region":{"startLine":84}}}],"partialFingerprints":{"codehealthFindingId/v1":"5158205e710ed91fd976b3b2bb33364551367843de7851abe3bf807d858af720"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/BobsBuddy/BobsBuddyInvoker.swift:1214-1220 | HSTracker/BobsBuddy/BobsBuddyInvoker.swift:1225-1229 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/BobsBuddy/BobsBuddyInvoker.swift:1214\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1214}}}],"partialFingerprints":{"codehealthFindingId/v1":"68e318be1831a3c29d4e9389f9a94970a394697d4cc84463f663260ae730c286"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/DesperateMeasures.swift:19-23 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/GelbinsTriumph.swift:17-21 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/DesperateMeasures.swift"},"region":{"startLine":19}}}],"partialFingerprints":{"codehealthFindingId/v1":"435b1d82ef7e195da65291cdf96607335619b23dac94893cde19286a408933b1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/Secrets/SecretsManager.swift:299-303 | HSTracker/Hearthstone/Secrets/SecretsManager.swift:342-346 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/Secrets/SecretsManager.swift:299\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":299}}}],"partialFingerprints":{"codehealthFindingId/v1":"3fbacc900bca2587526a38411b16799300062d559bc4d586707bba5b7be0f550"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Logging/Game.swift:5165-5169 | HSTracker/Logging/Game.swift:5214-5218 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Logging/Game.swift:5165\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":5165}}}],"partialFingerprints":{"codehealthFindingId/v1":"ddd7247346009ed00ea0d46439a591930d2adce61af3748e4dd4f3010c26d7de"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Mono/MonoHelper.swift:857-861 | HSTracker/Mono/MonoHelper.swift:884-888 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MonoHelper.swift"},"region":{"startLine":857}}}],"partialFingerprints":{"codehealthFindingId/v1":"5363a6a5d7e8fe1850c8040b743f53f41d27bd00503d641ffd7f4cb272e397a1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Anomalies/AnomalyGuideBadgeTriggerView.swift:49-53 | HSTracker/UIs/Battlegrounds/Guides/Anomalies/AnomalyGuideMulliganTriggerView.swift:86-90 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Anomalies/AnomalyGuideBadgeTriggerView.swift:49\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Anomalies/AnomalyGuideBadgeTriggerView.swift"},"region":{"startLine":49}}}],"partialFingerprints":{"codehealthFindingId/v1":"ad07990d49beaf78961d2dbfaae1fb9817a664b60b571439cba497cfe388e0dd"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Heroes/BattlegroundsHeroGuideViewModel.swift:36-40 | HSTracker/UIs/Battlegrounds/Guides/Quests/BattlegroundsQuestGuideViewModel.swift:28-32 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Heroes/BattlegroundsHeroGuideViewModel.swift:36\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Heroes/BattlegroundsHeroGuideViewModel.swift"},"region":{"startLine":36}}}],"partialFingerprints":{"codehealthFindingId/v1":"e3de89559c31b5de5a1195157a3acc35783ccdc83b82b00ba321ed5e4e924ad4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsCardsGroupView.swift:420-424 | HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsCardsGroupView.swift:460-464 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsCardsGroupView.swift:420\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsCardsGroupView.swift"},"region":{"startLine":420}}}],"partialFingerprints":{"codehealthFindingId/v1":"37af4dcc412465cdb8aa932e4e7b7439eb1990952b91105aa015a3e19d29359c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:76-80 | HSTracker/UIs/Battlegrounds/MinionPinning/BattlegroundsMinionPinningView.swift:367-371 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift:76\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Minions/BattlegroundsMinionsExtraFiltersView.swift"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"6c6eba1cdec4fad059f1572337e8058748ab187804bab8d4cab934d0ca758614"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Inspiration/BattlegroundsInspirationView.swift:207-211 | HSTracker/UIs/Battlegrounds/Inspiration/BattlegroundsInspirationView.swift:293-297 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Inspiration/BattlegroundsInspirationView.swift:207\u0060 it begins part-way through the construct above it, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Inspiration/BattlegroundsInspirationView.swift"},"region":{"startLine":207}}}],"partialFingerprints":{"codehealthFindingId/v1":"db38cf94954f9e8bd9fc986b8d44a35a9b8a29325b34a732c8b4daaec29b637f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:47-51 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:114-118 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift"},"region":{"startLine":47}}}],"partialFingerprints":{"codehealthFindingId/v1":"979468bb8643065ca822aced5ba4e5a5002a263c55530ed8f4103ec0f2ee4290"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:83-87 | HSTracker/UIs/Overlay/HDTCanvasIcon.swift:149-153 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:83\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The \u0060return\u0060 at the foot of the matched lines is the enclosing body\u0027s own terminal exit, not an early one: it moves with them unchanged, and each site calls the extracted unit from the position that \u0060return\u0060 occupied \u2014 no decision has to be handed back and re-acted on. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift:80\u0060 calls \u0060addCurve\u0060, \u0060p\u0060, \u0060addLine\u0060 and \u0060HSTracker/UIs/Overlay/HDTCanvasIcon.swift:148\u0060 does not \u2014 after which the two agree again for 2 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyView.swift"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"72ce1be407838b998980f7df4febb4b9f974a8dcf7fe3c2378cca23552070e32"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Cards/CardBar.swift:564-568 | HSTracker/UIs/Cards/CardBar.swift:574-578 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Cards/CardBar.swift:564\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":564}}}],"partialFingerprints":{"codehealthFindingId/v1":"5f5fe1ed49ca6951c30cea6ed01ce081a168790cce91927750fe7b6ee5bfa9c4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:250-254 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:261-265 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift:250\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetView.swift"},"region":{"startLine":250}}}],"partialFingerprints":{"codehealthFindingId/v1":"43c63cbfd36b9e008de1749083b3d4cd0cf261d5157f520c4e7c5db57073e848"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 11): HSTracker/Hearthstone/CounterSystem/Counters/CapturedArchmageCounter.swift:23-31 | HSTracker/Hearthstone/CounterSystem/Counters/CardsPlayedFor2ManaCounter.swift:24-31 | HSTracker/Hearthstone/CounterSystem/Counters/DiscoversThisGameCounter.swift:26-32 | HSTracker/Hearthstone/CounterSystem/Counters/GalakrondCounter.swift:31-38 | HSTracker/Hearthstone/CounterSystem/Counters/HeraldCounter.swift:55-63 | HSTracker/Hearthstone/CounterSystem/Counters/HolySpellsPlayedCounter.swift:24-30 | HSTracker/Hearthstone/CounterSystem/Counters/InfestTheSculleryCounter.swift:25-32 | HSTracker/Hearthstone/CounterSystem/Counters/PaladinCardsPlayedCounter.swift:24-30 | HSTracker/Hearthstone/CounterSystem/Counters/SelfDamageCounter.swift:34-41 | HSTracker/Hearthstone/CounterSystem/Counters/SpellsPlayedInCharactersCounter.swift:24-30 | HSTracker/Hearthstone/CounterSystem/Counters/TimethiefRafaamCounter.swift:36-42 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 11 call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/CapturedArchmageCounter.swift"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"21dc2c0bc784d2170f1c6a7f86334ae24b4b1ee5a4d1a01f48cb1cdfb37112f5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 11): HSTracker/Hearthstone/CounterSystem/Counters/CardsDrawnThisGameCounter.swift:41-47 | HSTracker/Hearthstone/CounterSystem/Counters/DeathwingDiscount.swift:35-41 | HSTracker/Hearthstone/CounterSystem/Counters/DiscoversThisGameCounter.swift:34-40 | HSTracker/Hearthstone/CounterSystem/Counters/HeraldCounter.swift:65-71 | HSTracker/Hearthstone/CounterSystem/Counters/HolySpellsPlayedCounter.swift:32-38 | HSTracker/Hearthstone/CounterSystem/Counters/ImbueCounter.swift:44-50 | HSTracker/Hearthstone/CounterSystem/Counters/MinionsDiedThisGameCounter.swift:35-41 | HSTracker/Hearthstone/CounterSystem/Counters/PlayedDragonsCounter.swift:36-42 | HSTracker/Hearthstone/CounterSystem/Counters/PlayedSpellsCounter.swift:56-62 | HSTracker/Hearthstone/CounterSystem/Counters/SilverHandRecruitBuffCounter.swift:35-41 | HSTracker/Hearthstone/CounterSystem/Counters/SpellsPlayedInCharactersCounter.swift:32-38 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 11 call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/CardsDrawnThisGameCounter.swift"},"region":{"startLine":41}}}],"partialFingerprints":{"codehealthFindingId/v1":"f16eb2ba12f9d34509752035d99662b46266d8f11b888a15f8d63c4956e1d9a8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift:27-35 | HSTracker/Hearthstone/CounterSystem/Counters/PogoHopperCounter.swift:27-35 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"51a64973c743b28198e8d1a9c7929f373544e309b1b1292289034e3a87a38289"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 5): HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift:53-58 | HSTracker/Hearthstone/CounterSystem/Counters/LibramCostReductionCounter.swift:52-57 | HSTracker/Hearthstone/CounterSystem/Counters/PaladinCardsPlayedCounter.swift:32-37 | HSTracker/Hearthstone/CounterSystem/Counters/ProtossMinionCostReductionCounter.swift:51-56 | HSTracker/Hearthstone/CounterSystem/Counters/SelfDamageCounter.swift:43-48 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 5 call sites, so a change lands once. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just after the matched lines, \u0060HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift:61\u0060 calls \u0060String\u0060, \u0060localizedString\u0060 and \u0060HSTracker/Hearthstone/CounterSystem/Counters/PaladinCardsPlayedCounter.swift:41\u0060 does not \u2014 after which the two agree again for 3 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ColossusCounter.swift"},"region":{"startLine":53}}}],"partialFingerprints":{"codehealthFindingId/v1":"007185f2f5c8ac13c2ac1b16240c2ab0bf5cade85a470e3b45d091cf43464af0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/ElwynnBoarCounter.swift:25-35 | HSTracker/Hearthstone/CounterSystem/Counters/HeroPowersThisGameCounter.swift:24-30 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ElwynnBoarCounter.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"ac04ba5cb4ee4bd4496f1cc6d769dbc6a454ed9119d9f7ac8d7af0bcd6cbfbab"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 4): HSTracker/Hearthstone/CounterSystem/Counters/ExcavateTierCounter.swift:57-64 | HSTracker/Hearthstone/CounterSystem/Counters/JadeDisplayCounter.swift:25-32 | HSTracker/Hearthstone/CounterSystem/Counters/JadeGolemCounter.swift:44-51 | HSTracker/Hearthstone/CounterSystem/Counters/LibramCostReductionCounter.swift:43-50 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 4 call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ExcavateTierCounter.swift"},"region":{"startLine":57}}}],"partialFingerprints":{"codehealthFindingId/v1":"ef8ef77eb2de390d57e60734d783b67b21bffe1f55810ac132f7b366060463b7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/MinionTypesPlayedThisGameCounter.swift:46-52 | HSTracker/Hearthstone/CounterSystem/Counters/PlayedSpellSchoolsCounter.swift:45-51 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/MinionTypesPlayedThisGameCounter.swift"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"1373a59220d3caf03e7b580c46937a842ffd40fb9ae65eb6687fb0b6b57566d8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/PlayedDragonsCounter.swift:28-34 | HSTracker/Hearthstone/CounterSystem/Counters/SummonedDragonsCounter.swift:31-39 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/PlayedDragonsCounter.swift"},"region":{"startLine":28}}}],"partialFingerprints":{"codehealthFindingId/v1":"e25f74ead377bbdec163912e47981688d9ec4119d8e851d9c8cb214c5a2b9291"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Settings/CounterVisibilitySettings.swift:100-109 | HSTracker/Hearthstone/RelatedCardsSystem/Settings/RelatedCardVisibilitySettings.swift:107-116 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Settings/CounterVisibilitySettings.swift"},"region":{"startLine":100}}}],"partialFingerprints":{"codehealthFindingId/v1":"7e779607aff9f3a7be0cc3ce666436066a1ed82821e43ead3e5df7d8c15920e1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/GorishiStinger.swift:22-26 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/InsectClaw.swift:22-26 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/GorishiStinger.swift"},"region":{"startLine":22}}}],"partialFingerprints":{"codehealthFindingId/v1":"25b486078cbe1a993f70f059a077161d81fa9f3ce9516316e5feec3490ff5070"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/GuitarSoloist.swift:20-26 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Hunter/BargainBin.swift:14-20 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/GuitarSoloist.swift"},"region":{"startLine":20}}}],"partialFingerprints":{"codehealthFindingId/v1":"a6b34207041e6ae8c35a67b94dbf11e983d94976f235c9e72bbfcc28be832383"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/Symbiosis.swift:16-20 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/StickUp.swift:15-19 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/Symbiosis.swift"},"region":{"startLine":16}}}],"partialFingerprints":{"codehealthFindingId/v1":"64c4223b33d81a4cad91cacf7b53fa800bcc36069218a41871d9d457917d7577"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Mage/WatercolorArtist.swift:18-22 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Neutral/HeraldOfLokholar.swift:16-20 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Mage/WatercolorArtist.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"4aea93cb40d9d3e6133d81dd6070149388bd13399c76e35bd9c5b82014fa5b4a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Neutral/PrimordialStudies.swift:15-19 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/LuckyComet.swift:15-19 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Neutral/PrimordialStudies.swift"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"6ea6a12a99ecdcad5bcb61f35c2569ff1a62269947acd02f0bf464a3c6f1ac26"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/HiHoSilverwing.swift:18-22 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/KnightOfAnointment.swift:18-22 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/HiHoSilverwing.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"e2de089649809437b0b82ed548babf0534c8aadd3b33287a899c67bc10cec85a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/ReinforcementAura.swift:17-21 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/ScarletRecruiter.swift:17-21 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/ReinforcementAura.swift"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"4d4892f1b00e36a19018b4769dc5d01a3951f48b31e590b798977f3296d5a809"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/NefersetWeaponsmith.swift:17-21 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/StolenSteel.swift:15-19 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/NefersetWeaponsmith.swift:14\u0060 calls \u0060picks\u0060, \u0060isWithReplacement\u0060 and \u0060HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/StolenSteel.swift:13\u0060 does not \u2014 after which the two agree again for 3 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/NefersetWeaponsmith.swift"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"04b24b06bbf636cd4ca1b3a2f74cb76a916d9ce568835bc25ef028fe202cd4a2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Shaman/Cinderfin.swift:22-26 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Shaman/SizzlingSwarm.swift:22-26 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Shaman/Cinderfin.swift"},"region":{"startLine":22}}}],"partialFingerprints":{"codehealthFindingId/v1":"9b105cc4994f621a6f898278912fcd5a064856ce1c0f79f74da4274ffac7c83d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Warlock/SketchArtist.swift:26-30 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Warlock/TamsinRoame.swift:16-20 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Warlock/SketchArtist.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"b782a15e2f5ba43eedef39c8eaa9c62f3c8c6220b4ba56cd473dbf922fe9b8b5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 3): HSTracker/UIs/Arena/ArenaPreDraftViewModel.swift:172-177 | HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift:117-122 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetViewModel.swift:118-125 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftViewModel.swift"},"region":{"startLine":172}}}],"partialFingerprints":{"codehealthFindingId/v1":"8a0daadc931b4c257e023526ac7df9cc44950980c3bc8ca68c983df08296a256"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 6): HSTracker/UIs/Battlegrounds/BobsBuddy/BobsBuddyPanelViewModel.swift:491-497 | HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingViewModel.swift:112-118 | HSTracker/UIs/Battlegrounds/Notifications/BattlegroundsNotificationsViewModel.swift:86-92 | HSTracker/UIs/Battlegrounds/QuestPicking/BattlegroundsQuestPickingViewModel.swift:50-56 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsTrinketPickingViewModel.swift:109-115 | HSTracker/UIs/Constructed/Mulligan/MulliganToastViewModel.swift:91-97 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 6 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 6 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BobsBuddy/BobsBuddyPanelViewModel.swift"},"region":{"startLine":491}}}],"partialFingerprints":{"codehealthFindingId/v1":"b3b2f0dfbae239ef4421befbc604e304e295f1d8f7662b367d083c3bb3b3dfbc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift:764-777 | HSTracker/UIs/Overlay/GuideTooltip.swift:262-275 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift"},"region":{"startLine":764}}}],"partialFingerprints":{"codehealthFindingId/v1":"32d4ef8ce3e8e114807e4b85546e1b64d6c110d978eed93da04eb378aa7fe0d6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:75-82 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift:92-99 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift\u0060 and \u0060HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift\u0060 as WHOLE FILES: this scan already matched 5 separate duplicated blocks between them, totalling at least 46 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift"},"region":{"startLine":75}}}],"partialFingerprints":{"codehealthFindingId/v1":"554d7000bccfc615dd78592bb53f8ae7815d27a238c77733f9d64d5dc24dda1b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:85-89 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift:102-106 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift\u0060 and \u0060HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsSingleTrinketView.swift\u0060 as WHOLE FILES: this scan already matched 5 separate duplicated blocks between them, totalling at least 46 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. The two sit in different directories, so one cannot simply be deleted in favour of the other while both are reached separately: hoist the shared part into a location both already depend on and have each file call it, and retire whichever file turns out to have no caller of its own left. Extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift"},"region":{"startLine":85}}}],"partialFingerprints":{"codehealthFindingId/v1":"72d6b697827b2328073aa1b224dacd161dcb3b4b429f651dac7d21aba28d52cc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift:151-156 | HSTracker/UIs/Battlegrounds/QuestPicking/BattlegroundsSingleQuestView.swift:132-137 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroHeaderView.swift"},"region":{"startLine":151}}}],"partialFingerprints":{"codehealthFindingId/v1":"7450b6c26da96f084ca1a2ed20cc1c8ef1d51ee5dc4bf478277b5d2192325fea"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingView.swift:76-83 | HSTracker/UIs/Battlegrounds/QuestPicking/BattlegroundsQuestPickingView.swift:66-73 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsTrinketPickingView.swift:79-86 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingView.swift"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"54c660e0f341a0492b5ff4e89068c6c2471562bf4296a5f56670d4f02753f968"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 3): HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingViewModel.swift:21-32 | HSTracker/UIs/Battlegrounds/QuestPicking/BattlegroundsQuestPickingViewModel.swift:23-32 | HSTracker/UIs/Battlegrounds/TrinketPicking/BattlegroundsTrinketPickingViewModel.swift:21-30 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingViewModel.swift"},"region":{"startLine":21}}}],"partialFingerprints":{"codehealthFindingId/v1":"705a6d293dd31fb77a6d7c36af36472da240e675383f6a731543a63947d56120"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionOverlayView.swift:108-117 | HSTracker/UIs/Overlay/Trackers/SecretsPanelView.swift:99-106 | HSTracker/UIs/Overlay/Trackers/TrackerPanelView.swift:179-186 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionOverlayView.swift"},"region":{"startLine":108}}}],"partialFingerprints":{"codehealthFindingId/v1":"b359e0d7733ec889113becd0cee1f93708f71794a22578df63253a3efa6a8ee7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift:74-79 | HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetViewModel.swift:84-89 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":74}}}],"partialFingerprints":{"codehealthFindingId/v1":"cb902163450fe9292086d5e77cc5d28630f162854bb5896d19f60e3d8347039a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/UIs/Battlegrounds/Tier7/Tier7Trial.swift:44-49 | HSTracker/UIs/Constructed/Mulligan/MulliganGuideTrial.swift:97-102 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7Trial.swift"},"region":{"startLine":44}}}],"partialFingerprints":{"codehealthFindingId/v1":"ef59b711ce6548e3be6ccff449777b3ca4811532075fd177d8fb3467139969f9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Cards/CardBar.swift:148-158 | HSTracker/UIs/Cards/ClassicBar.swift:17-27 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":148}}}],"partialFingerprints":{"codehealthFindingId/v1":"101ad323429ecbb532fee1e29374bfd13b059b0de851c2a5eae615fe8160e231"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Cards/CardBar.swift:870-876 | HSTracker/UIs/Toast/Toast.swift:162-168 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":870}}}],"partialFingerprints":{"codehealthFindingId/v1":"612866f887fbab34eeab9f8f88586bf5560127f48dcd06c4eb762d20cb78a6c1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): HSTracker/UIs/Constructed/ActiveEffects/ActiveEffectsOverlayView.swift:113-117 | HSTracker/UIs/Constructed/PlayerResourcesWidget/PlayerResourcesView.swift:110-114 | HSTracker/UIs/Counters/CountersOverlayView.swift:150-154 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/ActiveEffects/ActiveEffectsOverlayView.swift"},"region":{"startLine":113}}}],"partialFingerprints":{"codehealthFindingId/v1":"d1bc0be5f03ea312a8b2672bc4285006caf0b226aa1b0ff3340b03553889f3b0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): HSTracker/UIs/Preferences/GamePreferences.swift:104-112 | HSTracker/UIs/Preferences/InitialConfiguration.swift:109-117 \u2014 before extracting anything, compare \u0060HSTracker/UIs/Preferences/GamePreferences.swift\u0060 and \u0060HSTracker/UIs/Preferences/InitialConfiguration.swift\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 33 lines, which is the signature of one file having been copied from the other rather than of a helper waiting to be extracted. If that is what happened, the fix is to keep one copy and have the other call it (or delete it), which resolves this row and its siblings together \u2014 extracting one helper per block leaves the fork in place."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/GamePreferences.swift"},"region":{"startLine":104}}}],"partialFingerprints":{"codehealthFindingId/v1":"d5e785086d81d891cb14fe6365d61e7aeffa968ffaca4974e151bd65bfb4d552"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): HSTracker/UIs/Tooltips/RelatedCardsBrowserPanel.swift:243-250 | HSTracker/UIs/Tooltips/RelatedCardsTooltipPanel.swift:121-128 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Tooltips/RelatedCardsBrowserPanel.swift"},"region":{"startLine":243}}}],"partialFingerprints":{"codehealthFindingId/v1":"91243c08c328fffbc699703cbadbfbcd449d92bb51814d054cff3dbe547a8038"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): HSTracker/Hearthstone/BattlegroundsBoardState.swift:39-45 | HSTracker/Hearthstone/BattlegroundsDeityState.swift:80-85 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/BattlegroundsBoardState.swift"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"68734a0fee2c4b2afaf5105b48c9defc7a1d126a789e65106d7691d4b92d52e8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/Jumpscare.swift:15-19 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Hunter/DetailedNotes.swift:15-19 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/DemonHunter/Jumpscare.swift"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"1e8f68817615f3f8ead846159a4ab12611ada1589d988cd89fddff4074860b48"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/PartnerAssignment.swift:18-22 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/SpitefulChef.swift:17-21 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. \u2605 These copies have DRIFTED, and that is worth reading before extracting anything: just before the matched lines, \u0060HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/PartnerAssignment.swift:15\u0060 calls \u0060eventCount\u0060 and \u0060HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/SpitefulChef.swift:14\u0060 does not \u2014 after which the two agree again for 3 more lines. One of those two behaviours is the intended one and the other is what a copy-paste left behind, so decide which BEFORE unifying them: extracting the shared part will silently settle it, and if the copy that skips the call is the wrong one, that bug is already live."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/PartnerAssignment.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"e6d95dc27f16581dcee4cdeb9cf0751bfa5f8274b3789b19ea8e7842a5001c55"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 3): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Hunter/PastSilvermoon.swift:18-23 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/PastGnomeregan.swift:18-23 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Priest/PastConflux.swift:18-23 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 3 call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made 3 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Hunter/PastSilvermoon.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"fdca5c72916b1150f4934c8ce140bdd91e4fabee77ca767581c157b7805a2100"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/AssemblyLine.swift:15-19 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/Reconnaissance.swift:15-19 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Rogue/AssemblyLine.swift"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"a4c091a9825bc221573af2946c58fb29291b8178c81501521a76d353be8af516"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 3): HSTracker/Mono/QuestDataProxy.swift:24-33 | HSTracker/Mono/SimulationRunner.swift:42-51 | HSTracker/Mono/SimulatorProxy.swift:52-61 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from all 3 call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/QuestDataProxy.swift"},"region":{"startLine":24}}}],"partialFingerprints":{"codehealthFindingId/v1":"92ab73d80e7a82e51791f506b598d62cc484d4f861f93e100249c8bb055b7596"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): HSTracker/UIs/Overlay/Board/MercenariesAbilityIconView.swift:197-208 | HSTracker/UIs/Preferences/RelatedCardsPreferences.swift:243-254 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Board/MercenariesAbilityIconView.swift"},"region":{"startLine":197}}}],"partialFingerprints":{"codehealthFindingId/v1":"b1689df2c977ce96b74a19fa1cdb55da05965e1bbf7d6385b6d38e0eabbabf87"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Preferences/CountersPreferences.swift:218-228 | HSTracker/UIs/Preferences/RelatedCardsPreferences.swift:164-174 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/CountersPreferences.swift"},"region":{"startLine":218}}}],"partialFingerprints":{"codehealthFindingId/v1":"6ac3ca0bf9625846326dccf8cfca1df4c803fc3a31e8699f72a822fe3aa689be"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift:45-49 | HSTracker/Hearthstone/CounterSystem/Counters/PogoHopperCounter.swift:46-50 \u2014 the copies sit in sibling files of one directory, so a shared home is within easy reach: extract the block into a single shared function the call sites can all reach \u2014 a file they already depend on, or a new one alongside them \u2014 and call it from both call sites, so a change lands once. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift:45\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift"},"region":{"startLine":45}}}],"partialFingerprints":{"codehealthFindingId/v1":"9fac85a53e0f60c7dfcc53279e948d1ba94041e1c0022965a76cdc0e5c53e9e2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/LifeCycle.swift:17-21 | HSTracker/Hearthstone/RelatedCardsSystem/Cards/Shaman/Revolve.swift:17-21 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Druid/LifeCycle.swift"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"abfd3e0fd309753223293338612bdff6513e30cb74e05b2ac69a5eec19efe879"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift:100-110 | HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift:115-125 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift:100\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. The matched lines also transfer control out of the body holding them, which cannot survive a move into a called unit unchanged: have the extracted unit return that decision and let each site act on it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPlaqueViewModel.swift"},"region":{"startLine":100}}}],"partialFingerprints":{"codehealthFindingId/v1":"d3aade5a59db07ca630f4c982583f591436ae95941a81f1697546c594dc66331"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Arena/ArenaPreDraftView.swift:337-343 | HSTracker/UIs/Constructed/Mulligan/V2/LinearGaugeView.swift:25-31 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere both call sites can already reach \u2014 a location they all depend on today, or a new shared one if there is none \u2014 and call it from each site; until then, every change has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftView.swift"},"region":{"startLine":337}}}],"partialFingerprints":{"codehealthFindingId/v1":"a854faa2b94527394468ef5ccf5bc1d2a2a83663d63219c00e78e846c3194b81"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift:169-173 | HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift:202-206 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift:169\u0060 it runs out through the closing brace of the declaration holding it \u2014 the window is that declaration\u0027s tail, not a fragment that begins part-way through something, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BattlegroundsStatsPlateChrome.swift"},"region":{"startLine":169}}}],"partialFingerprints":{"codehealthFindingId/v1":"a8d8bfe687d0b04ea7265768fdf533b8fbfa884bb37ec74c7bcc9aa33f530d87"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): HSTracker/UIs/Overlay/Trackers/TrackerCounterViews.swift:120-126 | HSTracker/UIs/Overlay/Trackers/TrackerCounterViews.swift:139-145 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited. Read the line range as the matched WINDOW rather than a finished unit: at \u0060HSTracker/UIs/Overlay/Trackers/TrackerCounterViews.swift:120\u0060 it does not close everything it opens, so those exact lines cannot be lifted as they stand \u2014 widen the region to the smallest complete statement or declaration that contains it, and extract that. Note first that the copies are not typed on the same thing: the declarations holding them bind \u0060background\u0060 to \u0060\u0022card-counter-frame.png\u0022\u0060 in one and \u0060\u0022player-chance-frame.png\u0022\u0060 in another, and the duplicated lines use it. The extracted unit therefore needs a parameter type that fits BOTH \u2014 their common supertype where they have one, or a new abstraction over them where they do not \u2014 and settling that is the step that comes BEFORE the extraction above. Where the two types are deliberately unrelated, the duplication is the price of that separation and the honest resolution is to record the decision rather than to extract."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Trackers/TrackerCounterViews.swift"},"region":{"startLine":120}}}],"partialFingerprints":{"codehealthFindingId/v1":"02b13ef786e9a226bcd9bf986e9dc8cc498327eba52b0d05601f8e96a716617a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): HSTracker/UIs/Tooltips/RelatedCardsTooltipPanel.swift:240-244 | HSTracker/UIs/Tooltips/RelatedCardsTooltipPanel.swift:245-249 \u2014 both copies are in the same file, so extract the block into one function there and call it from each site \u2014 the copies drift apart the first time only one of them is edited."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Tooltips/RelatedCardsTooltipPanel.swift"},"region":{"startLine":240}}}],"partialFingerprints":{"codehealthFindingId/v1":"9162758bb7ea5dbdf78b80c57a50ef4481521bf32b087320ff8a4c7faede217d"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: AppDelegate (LCOM4 15): AppDelegate\u0027s methods fall into 15 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 15 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":20}}}],"partialFingerprints":{"codehealthFindingId/v1":"0fef3f9363a502682b25f5a4129ef7f833379d3659cd09e47a75b55ed8ac36f4"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: Game (LCOM4 9): Game\u0027s methods fall into 9 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 9 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":36}}}],"partialFingerprints":{"codehealthFindingId/v1":"cf5d43ac209857910279bb886007df9698e79ab29760cf71a23c21d8f4acef79"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: BaseCounter (LCOM4 7): BaseCounter\u0027s methods fall into 7 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 7 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BaseCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"f0124adca7464ee95c551bd6b1f04835091e891bd7729c05ac701fe741015ccc"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: PreferencesSidebarViewController (LCOM4 7): PreferencesSidebarViewController\u0027s methods fall into 7 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 7 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/PreferencesWindowController.swift"},"region":{"startLine":169}}}],"partialFingerprints":{"codehealthFindingId/v1":"e85a9c35306712790966c221708832557f8f2c6c4ec6b47edcdebe72acab75b6"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: Tier7PreLobbyViewModel (LCOM4 7): Tier7PreLobbyViewModel\u0027s methods fall into 7 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 7 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"941e441f234573c4ebb4d0ebc32eba86c163a57e2509868ed1730ec3c5c9fc1d"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: MinionProxy (LCOM4 6): MinionProxy\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/MinionProxy.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"dfbe36bab0ab015518b5f5e0f26dd6d2ffd4d61b946768df625589aa71a4829e"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: Deck (LCOM4 6): Deck\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Models/Deck.swift"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"105835d47d89606fc96b4c1db76ca1755430e741f912b73aeee784fa1c9c33fc"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: CardBar (LCOM4 6): CardBar\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"ab73e9ddd74d96d3a87a624e445ee465f5442f0f6034f242549d3a7c7abdfabb"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ConstructedMulliganPreLobbyWidgetViewModel (LCOM4 6): ConstructedMulliganPreLobbyWidgetViewModel\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganPreLobbyWidgetViewModel.swift"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"04ac3c8e29406f8bae79469bddee7c9de37c5572e961c9e57b1e7717412aac46"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: String (LCOM4 6): String\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/Regex.swift"},"region":{"startLine":55}}}],"partialFingerprints":{"codehealthFindingId/v1":"08d4ff0770aa0ada253abc2c29b1981d95b3fd2d4a5b27c2a05a7c4449af4506"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: CoreManager (LCOM4 5): CoreManager\u0027s methods fall into 5 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 5 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":29}}}],"partialFingerprints":{"codehealthFindingId/v1":"88e7b9b972a62313f011fafdedecf9d18d6cfdfb89861ca92ba12d384a2f70b6"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: Entity (LCOM4 5): Entity\u0027s methods fall into 5 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 5 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Entity.swift"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"da7d8d22ed5e3c3845480e1ea87f692c1142e020ef3696c39571bc13dba8ec89"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ArenaPreDraftViewModel (LCOM4 5): ArenaPreDraftViewModel\u0027s methods fall into 5 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 5 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Arena/ArenaPreDraftViewModel.swift"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"69a7ea1852a9bb151a07c4cf604c84e9f3bf0d1c015be59272320ef6fe0cb724"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ClassicBar (LCOM4 5): ClassicBar\u0027s methods fall into 5 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 5 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/ClassicBar.swift"},"region":{"startLine":12}}}],"partialFingerprints":{"codehealthFindingId/v1":"07d0c3124d98923bc24441262b8315a5c1c334c4df6d2e702cc5530f007cfe82"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ToastPanel (LCOM4 5): ToastPanel\u0027s methods fall into 5 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 5 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Toast/Toast.swift"},"region":{"startLine":78}}}],"partialFingerprints":{"codehealthFindingId/v1":"eaf01537e458418f02bc6bacdb70029fcf3ae89c768b797995142bceb1283075"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: InputProxy (LCOM4 4): InputProxy\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Mono/InputProxy.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"b2db4f5fb946951406c3e573c956d43041680641f0d955fa2979b45783e12a3d"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: DeckManager (LCOM4 4): DeckManager\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/DeckManager.swift"},"region":{"startLine":30}}}],"partialFingerprints":{"codehealthFindingId/v1":"98db42dea79c8b4c76f29f21c8f707dbcc8c85ca6615e65a05b2ca1b49a267f9"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: EditDeck (LCOM4 4): EditDeck\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/DeckManager/EditDeck.swift"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"b0639366df012444a4f71ec06cec3993936a122e94577d8b81ad2b130d7de271"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: BgsTooltipAnchorNSView (LCOM4 4): BgsTooltipAnchorNSView\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/GuideTooltip.swift"},"region":{"startLine":255}}}],"partialFingerprints":{"codehealthFindingId/v1":"7b94ffeccedf39300ac1d1e0802929909c3072e8d7e2c02390c4e39961b96d33"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: BloodGemBarrageBuffCounter (LCOM4 4): BloodGemBarrageBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/BloodGemBarrageBuffCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"4a8663c7a4055bc38d8d128e21908128ea5cbb266cd0e68186e281b196816a95"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: BloodGemCounter (LCOM4 4): BloodGemCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/BloodGemCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"0fa5581aa1d5189f9cf6ab7cc18af3de5bb0f21774bee53f3c6e4160646fb62b"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: DeitySizeCounter (LCOM4 4): DeitySizeCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/DeitySizeCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"1d813908512f24ad1fe444e9ad3ee74cff3ff20630b2876835efa562ca64a74d"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ElementalExtraStatsCounter (LCOM4 4): ElementalExtraStatsCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/ElementalExtraStatsCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"c76f84a179ab3e549363e95e21acbde6148ff0e43b0443b5130bc78cc6892e8c"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ElementalTavernBuffStatsCounter (LCOM4 4): ElementalTavernBuffStatsCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/ElementalTavernBuffStatsCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"ccc21f44bc0541bf3dedead0dcd83982cec4626fb8da8f552b585e977b1e2705"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: FreeRefreshCounter (LCOM4 4): FreeRefreshCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/FreeRefreshCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"6d8876e2340129ec168c989fdf358f821df0aaa3da18254ac271f654be8babae"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: NextRefreshDemonFodderCounter (LCOM4 4): NextRefreshDemonFodderCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/NextRefreshDemonFodderCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"c753c3175c7e2988811877155b0c2c91625411c9bfa0951df9584644f6294774"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: RandomTavernMinionBuffCounter (LCOM4 4): RandomTavernMinionBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/RandomTavernMinionBuffCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"ddeab307e7951a17c7ed28211b96553377ec0ce309e055382dc3fa6f1445dbb7"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: TavernSpellsBuffCounter (LCOM4 4): TavernSpellsBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/TavernSpellsBuffCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"6c0755d5510858a02906021fcbda4388e4bce345ce97b055088b8c190b50ff65"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: UndeadBuffCounter (LCOM4 4): UndeadBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/UndeadBuffCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"7190da2926c046a8565e63320db364ad94d5b51371a380ba49cad99285bef44a"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: VolumizerBuffCounter (LCOM4 4): VolumizerBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/VolumizerBuffCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"b93819af900a9ffeb7cbd4831d812019ed1c33532f758286f40bcd99e43545ca"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: WhelpStatsBuffCounter (LCOM4 4): WhelpStatsBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/WhelpStatsBuffCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"e82885182e306d2e615a2d51a844c37e05b9d9b006700e7562587faa01106d88"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: AbyssalCurseCounter (LCOM4 4): AbyssalCurseCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AbyssalCurseCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"9681d3430af5cf61e0dca5f0d755c293c4662f5d0113d068c938497fe15c95e7"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: AsteroidExtraDamageCounter (LCOM4 4): AsteroidExtraDamageCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AsteroidExtraDamageCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"09ceacfbe88cc242ac259599def8ab303f62c75d11f641a0d1f97c8af99bbf1d"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: DeathwingDiscount (LCOM4 4): DeathwingDiscount\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/DeathwingDiscount.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"bf6af4c3b62846cdc8770a03a71c401430c9a308957e751507df647c1aa4056a"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: DiveTheGolakkaDepthsBuffCounter (LCOM4 4): DiveTheGolakkaDepthsBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/DiveTheGolakkaDepthsBuffCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"4c43961eb27a437df575dbf9ecc707c87683e2b82c60d0957ba58771d8a20bb0"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ExcavateTierCounter (LCOM4 4): ExcavateTierCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ExcavateTierCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"c4b1550bd2a1d905a8507891c9a6e829cf32e9bbf5f860afff7e3066ef06ff9e"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ExtraAnimalCompanionCounter (LCOM4 4): ExtraAnimalCompanionCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ExtraAnimalCompanionCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"e7eaae9adaa246e9c2781c597481513474d420bd1b2404804a4776a5aacb2948"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: JadeGolemCounter (LCOM4 4): JadeGolemCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/JadeGolemCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"b3f753cb44547ed7fd26cccea9c2eddf8caae482eab8cf892c56052ae039e95e"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: KiljaedenCounter (LCOM4 4): KiljaedenCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/KiljaedenCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"1bc50b852c651e4a81da604c30dc63d6ba9f6b4b2728e9003a14cd09c7194d0b"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: LeylineCostReductionCounter (LCOM4 4): LeylineCostReductionCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/LeylineCostReductionCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"aa1c2633b6aeeecbf73a6693921e023771340571b0a0d4587c81d05b1926a360"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: LeylineEffectIncreaseCounter (LCOM4 4): LeylineEffectIncreaseCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/LeylineEffectIncreaseCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"5f48d63280ff299fdec79fb6ba80912f713b65cb96c82dd23799c009f14302cc"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: LeylineExtraTriggerCounter (LCOM4 4): LeylineExtraTriggerCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/LeylineExtraTriggerCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"009a8b38157a1999188b8e580e44627a0801272c96bfc0af9a6d4dc89dc63d64"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: ProtossMinionCostReductionCounter (LCOM4 4): ProtossMinionCostReductionCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ProtossMinionCostReductionCounter.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"acf269228b4ba73768a4fe8957b35fe22f9c6803d2494d8c00e2bd45cd71335e"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: SilverHandRecruitBuffCounter (LCOM4 4): SilverHandRecruitBuffCounter\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/SilverHandRecruitBuffCounter.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"dcef42f5907399d2091a3dcd5ce5ff81a94736eb0264b8a7813c15e959c2fb59"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: BattlegroundsGuidesTabsViewModel (LCOM4 4): BattlegroundsGuidesTabsViewModel\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/BattlegroundsGuidesTabsViewModel.swift"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"06820f907c6a289a6289d7da1674f3117088c9df0a53714eaf27624e054e73f9"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: CardMarkerViewModel (LCOM4 4): CardMarkerViewModel\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Hand/CardMarkerViewModel.swift"},"region":{"startLine":14}}}],"partialFingerprints":{"codehealthFindingId/v1":"6772a601b78359724c007eb4fe33870a787537949cc69cb0a740b702210d0c41"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: TalanjiOfTheGraves (LCOM4 4): TalanjiOfTheGraves\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/DeathKnight/TalanjiOfTheGraves.swift"},"region":{"startLine":9}}}],"partialFingerprints":{"codehealthFindingId/v1":"91190284fcf65a1304b3380bf7105cfcf685a94158d81424ac43c40130ebc0be"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: LiftOff (LCOM4 4): LiftOff\u0027s methods fall into 4 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 4 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Multi/LiftOff.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"74f3a6604351c3f4c3a85ea7af819cdc321929a96f21d23148098b917dd50c3e"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testMonoClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"e0531e1fb7d46ba059cc7338ca5ea9a077877812be9ef457ede80b4ca33f3d5d"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testActiveEffectClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":87}}}],"partialFingerprints":{"codehealthFindingId/v1":"4c0e10bf8033422202d2ee0eb6c901bbb1689f244edbaba1b3bccd2c3f774db5"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testCounterClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"1fb87978546442d7cbe3ff10a989367fbf92283f7aa42bfd55bbfc9484997717"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testRelatedClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":95}}}],"partialFingerprints":{"codehealthFindingId/v1":"89f81628048a8978b5cb6b1546212e2a8fdb27c8424418ebd804df3e698533aa"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testHighlightClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":99}}}],"partialFingerprints":{"codehealthFindingId/v1":"1f17f298853cdd6ea8b23ce63cd4ea3a6fb88ca54a52897c44a7662abd4021c7"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testSpellSchoolTutorClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":103}}}],"partialFingerprints":{"codehealthFindingId/v1":"36bd25265864103d1957516efc554cda73ccf9f030518fce03a43e4d462a68b6"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: testCardGeneratorClasses: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/ReflectionHelperTests.swift"},"region":{"startLine":107}}}],"partialFingerprints":{"codehealthFindingId/v1":"78ba0ded6f7bfbd22a39478e5efa711b0537bba183287aca8169b52794f6acff"}},{"ruleId":"D12","level":"warning","message":{"text":"Floating branch dependency: customtooltip: Dependency \u0060customtooltip\u0060 is resolved in HSTracker.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved against branch \u0060main\u0060 rather than a released version \u2014 the project pins no immutable point, so resolving the packages again moves this dependency to whatever that branch holds at the time. Change the dependency rule in Xcode to a version requirement, or to an exact commit."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"cea72ad6aa5fd8f3ae579532fc48f8feb661bc908dd0528ec97a58d255e07f87"}},{"ruleId":"D13","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"800b947596ca0f42b410ef6dbceb8e1536ec70ad7b790037601b062496b95692"},"taxa":[{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D13","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"30ca7eb30a3a07250fb25ab14246fda150d9633b003f3a6020d4666d3a71b251"},"taxa":[{"id":"CWE-259","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-798","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Logging/Parsers/PowerGameStateParser.swift: HSTracker/Logging/Parsers/PowerGameStateParser.swift changed 32 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 636 in PowerGameStateParser.handle at line 91. 15 of those changes were fix/bug commits, and the other 17 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Logging/Parsers/PowerGameStateParser.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"5ad4748f425bd642689ab6551878d2e7c88d280fe125a885e3554bffd7816579"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/BobsBuddy/BobsBuddyInvoker.swift: HSTracker/BobsBuddy/BobsBuddyInvoker.swift changed 101 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 75 in BobsBuddyInvoker.setupInputPlayer at line 1171. 66 of those changes were fix/bug commits, so the churn is repair rather than feature work. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/BobsBuddy/BobsBuddyInvoker.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1171}}}],"partialFingerprints":{"codehealthFindingId/v1":"279b3c8b8f8ed9c7337299e843bb8bf03e2bde37519ddcceb7df67ecc6101bbc"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Logging/Game.swift: HSTracker/Logging/Game.swift changed 90 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 41 in Game.generateEndgameStatistics at line 2158. 22 of those changes were fix/bug commits, and the other 68 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Logging/Game.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2158}}}],"partialFingerprints":{"codehealthFindingId/v1":"e41433f0a6a5f42833860f729b8a5e23c8f6c05bd3c93da5b3a30247950e66db"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Logging/Parsers/TagChangeActions.swift: HSTracker/Logging/Parsers/TagChangeActions.swift changed 27 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 58 in TagChangeActions.findAction at line 21. 11 of those changes were fix/bug commits, and the other 16 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Logging/Parsers/TagChangeActions.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":21}}}],"partialFingerprints":{"codehealthFindingId/v1":"ce7525b868abdef14c305853286fa26200b9d49eae997dbd69ba2bd391b39f50"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift: HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift changed 31 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 32 in RootOverlayViewModel.setCardOpacityMask at line 137. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift"},"region":{"startLine":137}}}],"partialFingerprints":{"codehealthFindingId/v1":"2e6950d323a8700efcf391127637151b9c7721ebf3b09309afe97b76b38caf5e"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Database/Database.swift: HSTracker/Database/Database.swift changed 10 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 49 in Database.apply at line 167. 3 of those changes were fix/bug commits, and the other 7 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Database/Database.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Database/Database.swift"},"region":{"startLine":167}}}],"partialFingerprints":{"codehealthFindingId/v1":"6f59560cddb93f3a9b6f60e549e2e62f4d9e85c5fcebb8d85ff8679832c99d4f"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Preferences/BattlegroundsPreferences.swift: HSTracker/UIs/Preferences/BattlegroundsPreferences.swift changed 11 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 39 in BattlegroundsPreferences.checkboxClicked at line 106. 1 of those changes was a fix/bug commit, and the other 10 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Preferences/BattlegroundsPreferences.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/BattlegroundsPreferences.swift"},"region":{"startLine":106}}}],"partialFingerprints":{"codehealthFindingId/v1":"87404336890f209849cc2d42629169352a438cd86ca1064cc88c8423260619d1"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/AppDelegate.swift: HSTracker/AppDelegate.swift changed 18 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 23 in AppDelegate.applicationDidFinishLaunching at line 82. 3 of those changes were fix/bug commits, and the other 15 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/AppDelegate.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":82}}}],"partialFingerprints":{"codehealthFindingId/v1":"dca80016613d29dcd3881a116d1d129317cce904177a6c7e94f80cd0ab0f149a"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Utility/BattlegroundsDb.swift: HSTracker/Utility/BattlegroundsDb.swift changed 9 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 31 in BattlegroundsDb.update at line 223. 3 of those changes were fix/bug commits, and the other 6 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Utility/BattlegroundsDb.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BattlegroundsDb.swift"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"7b12d8b3b09b7c189a55e4ec1445f961d42d44b096f734dca24c4f119882099e"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Overlay/Root/RootOverlayWindow.swift: HSTracker/UIs/Overlay/Root/RootOverlayWindow.swift changed 17 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 16 in RootOverlayWindow.updateHoverTooltip at line 441. 1 of those changes was a fix/bug commit, and the other 16 changed it for other reasons \u2014 this file is under both repair and feature pressure. Before the next change lands here, make sure the area it touches is under test, then split that area out of the file so the following change is smaller than this one \u2014 a file this often edited pays the complexity back every time. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Overlay/Root/RootOverlayWindow.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayWindow.swift"},"region":{"startLine":441}}}],"partialFingerprints":{"codehealthFindingId/v1":"f0051ab530e7ae375cae369aa599d03a288944acd42bd0238e2b63882a013608"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Preferences/TrackersPreferences.swift: HSTracker/UIs/Preferences/TrackersPreferences.swift changed 8 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 29 in TrackersPreferences.checkboxClicked at line 109. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Preferences/TrackersPreferences.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Preferences/TrackersPreferences.swift"},"region":{"startLine":109}}}],"partialFingerprints":{"codehealthFindingId/v1":"3132e0a70eb384f5f3418151d9d82a58ccedcc356d4c96f371aa07d21462989b"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift: HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift changed 10 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 23 in CardTooltipPanel.show at line 305. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/CardImageTooltip.swift"},"region":{"startLine":305}}}],"partialFingerprints":{"codehealthFindingId/v1":"a5fbb154a499b2251740f631a5d722cc71597a0e90da2292a12d4f357141b0dc"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Logging/Handlers/LoadingScreenHandler.swift: HSTracker/Logging/Handlers/LoadingScreenHandler.swift changed 5 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 41 in LoadingScreenHandler.handle at line 39. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Logging/Handlers/LoadingScreenHandler.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/LoadingScreenHandler.swift"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca845c516c0e0f055927c4e44b2317c23cfe9fdc74bb7740d08f949a9b68fa15"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/HSReplay/Data/UploadMetaData.swift: HSTracker/HSReplay/Data/UploadMetaData.swift changed 4 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 37 in UploadMetaData.getPlayerInfo at line 179. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/HSReplay/Data/UploadMetaData.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/Data/UploadMetaData.swift"},"region":{"startLine":179}}}],"partialFingerprints":{"codehealthFindingId/v1":"d65169bc07fb5f6e0d35fc240ba991d17c3b9d36ca29783e18703197f44a9d72"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift: HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift changed 6 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 18 in ConstructedMulliganGuidePreLobbyViewModel.update at line 430. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift"},"region":{"startLine":430}}}],"partialFingerprints":{"codehealthFindingId/v1":"eb5cef1e65b47dc837a1db320267a4d37d41f448872b8438d88833dd8dee17ef"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/ImageUtils.swift: HSTracker/UIs/ImageUtils.swift changed 3 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 26 in ImageUtils.loadImage at line 143. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/ImageUtils.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/ImageUtils.swift"},"region":{"startLine":143}}}],"partialFingerprints":{"codehealthFindingId/v1":"6feea4ba9393df7c7c0cdceeea5947cae99711456091c9dabdbd2d9edaf6ccd5"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Battlegrounds/BattlegroundsMinionView.swift: HSTracker/UIs/Battlegrounds/BattlegroundsMinionView.swift changed 3 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 23 in BattlegroundsMinionView.draw at line 88. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Battlegrounds/BattlegroundsMinionView.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/BattlegroundsMinionView.swift"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"6433678161afcec72127a740694163bef9e53a7603d808490c0517e849406156"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Hearthstone/Secrets/SecretsManager.swift: HSTracker/Hearthstone/Secrets/SecretsManager.swift changed 4 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 17 in SecretsManager.getSecretsCreatedBy at line 223. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Hearthstone/Secrets/SecretsManager.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":223}}}],"partialFingerprints":{"codehealthFindingId/v1":"2fe0f37710caa66b24a13cb7b48553eef3568bc406fe2a932210136623eaa0d7"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Hearthstone/CounterSystem/Counters/AnimalCompanionCounter.swift: HSTracker/Hearthstone/CounterSystem/Counters/AnimalCompanionCounter.swift changed 3 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 22 in AnimalCompanionCounter.handleTagChange at line 59. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Hearthstone/CounterSystem/Counters/AnimalCompanionCounter.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/AnimalCompanionCounter.swift"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"3e5fd540db6fa7c5a254d43920265604411e1489c2f63ca706dfbabd03622e56"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift: HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift changed 4 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 16 in Tier7PreLobbyViewModel.update at line 182. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift"},"region":{"startLine":182}}}],"partialFingerprints":{"codehealthFindingId/v1":"0f4ad2d932c641c5a7b02e7899187804d5fd00c17efbc7d59bb4a5bd478a1d81"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/HearthWatcher/ArenaWatcher.swift: HSTracker/HearthWatcher/ArenaWatcher.swift changed 2 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 32 in ArenaWatcher.update at line 67. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/HearthWatcher/ArenaWatcher.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HearthWatcher/ArenaWatcher.swift"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"006ab0cb9ed19c44fddd6e553f9eb5c5ac3bf3a49b508506c29ab5b763393dbb"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Logging/LogReader.swift: HSTracker/Logging/LogReader.swift changed 2 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 23 in LogReader.readFile at line 92. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Logging/LogReader.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/LogReader.swift"},"region":{"startLine":92}}}],"partialFingerprints":{"codehealthFindingId/v1":"bfc552c57c80ad7a7dff3a301a659e5a747aadbfe413a0135cc22ec0e2a17d18"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: HSTracker/Hearthstone/CounterSystem/BgCounters/GoldNextTurnCounter.swift: HSTracker/Hearthstone/CounterSystem/BgCounters/GoldNextTurnCounter.swift changed 2 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 19 in GoldNextTurnCounter.handleTagChange at line 91. Frequent change and high complexity in one file compound: schedule the next change to it to include carving out the part being edited, with the area under test before it moves. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Hearthstone/CounterSystem/BgCounters/GoldNextTurnCounter.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/BgCounters/GoldNextTurnCounter.swift"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"85dcdb185e62542d90e3183414b7526c411f62904d8ba2a27af56fb8ede8e118"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: HSTracker/Logging/Entity.swift: HSTracker/Logging/Entity.swift changed 6 times in last 90 days and 4 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 6 (its worst body is Entity.description at line 282), UNDER the 15 threshold, so this is deliberately not filed as a churn \u00D7 complexity hotspot \u2014 the difficulty here is in the behaviour the file has to get right, not in its control flow, and refactoring it for complexity would be the wrong move. The repairs counted were: \u201Cfix: azalina soulsever info leak\u201D; \u201Cfix: keep trinket slot card id before reveal overwrites it\u201D; \u201Cfix: related cards not handling tranformed cards\u201D; \u201Cfix: beatrix mulligan\u201D. Each one is a case this code did not handle. Before the next change lands here, check that every one of them is pinned by a test that fails without its fix; where the same area keeps coming back, the durable fix is usually at the interface that keeps being misused rather than at the line that was last corrected. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/Logging/Entity.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Entity.swift"},"region":{"startLine":282}}}],"partialFingerprints":{"codehealthFindingId/v1":"e1a657e2a01732a677354863aba8d24f750663a51ab6363ba3b462884d59932e"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift: HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift changed 6 times in last 90 days and 4 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 6 (its worst body is CompGuideDetailView.body at line 21), UNDER the 15 threshold, so this is deliberately not filed as a churn \u00D7 complexity hotspot \u2014 the difficulty here is in the behaviour the file has to get right, not in its control flow, and refactoring it for complexity would be the wrong move. The repairs counted were: \u201Cfix: stop the comp guide truncating long names and crowding its headings\u201D; \u201CFix the comp guide being wider than the guides tab strip\u201D; \u201Cfix: comp guides never loaded in the Battlegrounds lobby\u201D; \u201Cfix: guide header art faded out too early\u201D. Each one is a case this code did not handle. Before the next change lands here, check that every one of them is pinned by a test that fails without its fix; where the same area keeps coming back, the durable fix is usually at the interface that keeps being misused rather than at the line that was last corrected. Counted over 2026-06-26..2026-09-24, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-06-26 17:55:14 -04:00\u0027 --until=\u00272026-09-24 17:55:14 -04:00\u0027 --full-history --no-merges -- HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift\u0060: merges are excluded because a merge re-states changes already counted at their own commits, and history is NOT path-simplified because a change that reached the file through a merged branch is still a change to it. That command counts raw commits and can read HIGHER than this row, which counts a cherry-picked re-land, and a revert together with the commit it undoes, once each \u2014 a difference of several commits on a file whose history was re-landed or reverted inside the window."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Guides/Comps/CompGuideDetailView.swift"},"region":{"startLine":21}}}],"partialFingerprints":{"codehealthFindingId/v1":"259120f9440acbbdb2b760fb674b96a0f84e8697d03a121fc23bfb95b17f4db1"}},{"ruleId":"D16","level":"note","message":{"text":"Off-boarding risk: anonymized user #1: If anonymized user #1 becomes unavailable, 140 significant file(s) lose their only recent owner: HSTracker/Logging/Game.swift, HSTracker/Logging/Parsers/PowerGameStateParser.swift, HSTracker/BobsBuddy/BobsBuddyInvoker.swift, HSTracker/Logging/Parsers/TagChangeActions.swift, HSTracker/Core/Settings.swift, HSTracker/Logging/Player.swift, HSTracker/Mono/MonoHelper.swift, HSTracker/Hearthstone/Secrets/SecretsManager.swift (\u002B132 more). Pair on, review, or document these before any departure."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"cfc2c3f55fae2a3bd51005833dd6af733c570a2d0387cb814c3811c8aca2be14"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: as this is implemented in BB and would require a call via Mono \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1185}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1ddb45378481ddc484c7ca6f713582d444975cbb40582b43f7b65400a67c5c8"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: [Duos] Check if friendly translates to player correctly \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/BobsBuddy/BobsBuddyInvoker.swift"},"region":{"startLine":1289}}}],"partialFingerprints":{"codehealthFindingId/v1":"6410d998931cbc438f957d86a8ee1045531ff8702cb6008844dc05bbc6489446"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Safely unwrap this later \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/Extensions/NSFont\u002BNSTextField.swift"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"eb5f03b05ecc5f663a78a0267a2e3c3106ed5f5021514265f3efbe68be87c4fd"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Better error handling \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/HSReplayAPI.swift"},"region":{"startLine":90}}}],"partialFingerprints":{"codehealthFindingId/v1":"ac70ad663de6189f6918e330522bf081a4c48594894dadf0d8e5288b969c89d3"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: sideboards \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/HearthDbConverter.swift"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"36f738424b0cb391f66f2a4a822883e0e81320014376a8b40c15041d56302fa6"}},{"ruleId":"D17","level":"warning","message":{"text":"FixmeComment: // FIXME: implement \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/EffectSystem/EntityBasedEffect.swift"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"524d1b4b380f75e4fe395a9c44deab6aa155d496b0fe5401e8b955c6a7b66384"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: use deck state to get highest and lowest cost \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Priest/ScaleReplica.swift"},"region":{"startLine":19}}}],"partialFingerprints":{"codehealthFindingId/v1":"812493bf04416563a80930c37deb616c146239cae4519fdaec0ffaa98249b3c9"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: break ties when Effigy \u002B Deathrattle played on the same turn \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/Secrets/SecretsManager.swift"},"region":{"startLine":655}}}],"partialFingerprints":{"codehealthFindingId/v1":"4e38ec4a9e98b0cbe9c6daf1537d77336133554ce90d3cbfbc6b716462469dcd"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: If we want to add more event tracking to HSTracker \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":2308}}}],"partialFingerprints":{"codehealthFindingId/v1":"0bdb4533d5b8b2cc51f83332744b98b98214eca68fe80db37c0d2c92ab3910ae"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: fix me \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3171}}}],"partialFingerprints":{"codehealthFindingId/v1":"e5d9b6c4a6339357b11f2c4ba9aa2d191ca4e830df4d7dca3b41f2e713d19616"}},{"ruleId":"D17","level":"warning","message":{"text":"FixmeComment: // FIXME: todo \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3185}}}],"partialFingerprints":{"codehealthFindingId/v1":"456590b00535dd54313ca4564d63e91af1cc326a82274955c0d408f1366518d1"}},{"ruleId":"D17","level":"warning","message":{"text":"FixmeComment: // FIXME: add \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3197}}}],"partialFingerprints":{"codehealthFindingId/v1":"c09edbfcd7dae44c25d2ac0a1a3f2d25f3f7f11b03e16f4eb88402b3f56d4373"}},{"ruleId":"D17","level":"warning","message":{"text":"FixmeComment: // FIXME: add \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3218}}}],"partialFingerprints":{"codehealthFindingId/v1":"49522d9e55e2d22ca9f0d0729a39b9b2544e5c1c601497971eebc31d5781be30"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: handle errors, exceptions \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":3479}}}],"partialFingerprints":{"codehealthFindingId/v1":"4ae65410884da63bdaa701268f211b5497ce2badf5e716e6679c8e667fad55e5"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: trial active \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":4453}}}],"partialFingerprints":{"codehealthFindingId/v1":"da3b73e140c0d5299a669739ea0d6310053b081f5ec696ba53b21f23d260c533"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: add observer here as well \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CoreManager.swift"},"region":{"startLine":500}}}],"partialFingerprints":{"codehealthFindingId/v1":"b579dd3cc602390a9348dac86152a7bc7990c1b732bd4abfb490cee58066f33c"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Add Demon Hunter \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CardIds/DefaultDecks.swift"},"region":{"startLine":93}}}],"partialFingerprints":{"codehealthFindingId/v1":"64496256a2696f8dd5e4a12fa83465ca1315c4c9020dbc022d97aba3c3a89a24"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // Todo: Add Demon Hunter \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CardIds/DefaultDecks.swift"},"region":{"startLine":117}}}],"partialFingerprints":{"codehealthFindingId/v1":"953f715a686fe1e24ccad0c902e69daa5f4da7c174e187deef0f40ec05302b65"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // Todo: Add Demon Hunter \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/CardIds/DefaultDecks.swift"},"region":{"startLine":131}}}],"partialFingerprints":{"codehealthFindingId/v1":"b1d23271177bdfa25b801c8aff835ef44962d5534c913060788dd0e3a67ab99f"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: remove set on most properties to ensure encapsulation \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/LogEventHandlers.swift"},"region":{"startLine":16}}}],"partialFingerprints":{"codehealthFindingId/v1":"a0fd48bd9547393763e4f865e49e3f732bfec291e041f0a97455611507fe8b82"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: this is essentially blocking the global queue! \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/TagChangeActions.swift"},"region":{"startLine":1501}}}],"partialFingerprints":{"codehealthFindingId/v1":"7785ba88d3f7de7cef2e1e4d43f6ebec59148e52ee8b01116187bf863be46cb8"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: //TODO: Gral, the Shark? \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":846}}}],"partialFingerprints":{"codehealthFindingId/v1":"f8aed4b763393fa5ed835a3c05b6573e044a7e906aa29bd4d5af8083b25f8e18"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: //CardIds.Collectible.Priest.HolyWater: -- TODO \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":1088}}}],"partialFingerprints":{"codehealthFindingId/v1":"1839470ebdaff03289e3c58ae8e44fddf24c86fb933cc1ddd66e0fb5cbdb0753"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: //TODO: Hex Lord Malacrass \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":1199}}}],"partialFingerprints":{"codehealthFindingId/v1":"b5c259c3ff6b5260d08b06d579e07a64974811e5a4332d747a2cb10c7613b288"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: //TODO: Krag\u0027wa, the Frog \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":1200}}}],"partialFingerprints":{"codehealthFindingId/v1":"5b37ec1ae435f959e0859e0a9ea5b3acff12104e8c0d8919fcf67b0ba9776e0d"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO Token2 if only one card is left \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Parsers/PowerGameStateParser.swift"},"region":{"startLine":1378}}}],"partialFingerprints":{"codehealthFindingId/v1":"7f46785a5d993011d602d17ff7b42b43230249e384e40ff47133039ee39a5242"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: run long simulation on cluster for better grid \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Statistics/LadderGrid.swift"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"8a26f7cfec2f7725c3449ff37223183c53dd203215023625c4c259bf99578971"}},{"ruleId":"D17","level":"warning","message":{"text":"FixmeComment: // FIXME \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/Session/BattlegroundsSessionViewModel.swift"},"region":{"startLine":371}}}],"partialFingerprints":{"codehealthFindingId/v1":"5f8ba7a2c6d1369d9c8ddc9e3d2ef9229b638dadaaf382756b536917a6c72138"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: HSReplayNetOAuth.AccountDataUpdated \u002B= () =\u003E Core.Overlay.UpdateMulliganGuidePreLobby(); \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift"},"region":{"startLine":136}}}],"partialFingerprints":{"codehealthFindingId/v1":"93ba48dc95ceda96fff75cf686c90ebe6789c753a58eec9856d83f91183c3b84"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: HSReplayNetOAuth.LoggedOut \u002B= () =\u003E Core.Overlay.UpdateMulliganGuidePreLobby(); \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Constructed/Mulligan/ConstructedMulliganGuidePreLobbyViewModel.swift"},"region":{"startLine":137}}}],"partialFingerprints":{"codehealthFindingId/v1":"14f45553409bece40f17d2e2d51627c92a9f676b4ebb56ba4a39b9964c88b5b7"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: //TODO: centering? \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/StatsManager/Statistics.swift"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"0e8119cbe15d590131cfa1b14c6b1ac0f61d57a3fb79b5add2dff710c11f3365"}},{"ruleId":"D17","level":"warning","message":{"text":"XxxComment: // XXX: This might be unsafe \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/StatsManager/Statistics.swift"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"69db6249fc6c6aee9fef7ded1b97023bc7e47c92bdc94c950b58448168fc7a08"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Localization \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/StatsManager/LadderTab.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"38f76b191c01aba539f711ffc22dd368faef8cfe2429d8494673fac40d70f7b8"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: use not defered gui instead \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Trackers/WindowManager.swift"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"2a8e9e34f1006bcf58df675cedad0b60428d8c32eaf1886960d015a9e6fcef09"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: this class is very messy, clean it up \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BoardDamage/EntityHelper.swift"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"1962c6db7d67dff30d42c99fec6a58c8d54459e09ef74162b30c53719dd3440d"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: if frozen on turn, may be able to attack next turn \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Utility/BoardDamage/BoardCard.swift"},"region":{"startLine":114}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1873847c2cce8a51d8f8a5990095f187226265dc8f3973338d41d1f8190afef"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Add test for Rat Trap, Hidden Wisdom, Sacred Trial, etc. \u2014 source code is not a task system: move the work to your tracker and leave a reference instead (e.g. \u0060// REF: #123\u0060), so the task is planned where tasks live and the ticket links back to the code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTrackerTests/SecretTests.swift"},"region":{"startLine":788}}}],"partialFingerprints":{"codehealthFindingId/v1":"5fc70064758669d3c9235e056e460cf0399e2a2b1ea15cd4b8daa82d992300fc"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no installation or build instructions: The installation section lists requirements but does not show how to run the app after setup. Add a short \u0027Run HSTracker\u0027 step under installation showing how to launch it from the Applications folder or via Spotlight."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"README.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1fa6bfcd5c6ae231d1b8ee99bced50b832d1f61cde9da6cbb533467b879a57db"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no usage examples: Usage is sketched in general terms (\u0027Create a new deck...or import it from a deckstring\u0027) without runnable commands. Add a usage example, such as the command-line or a one-command run snippet showing how to start HSTracker and load a deck."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"README.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1355eb4e127a4bc9236772ce1c46f09510aa9286e7a06d1e47f308229df19049"}},{"ruleId":"D20","level":"note","message":{"text":"No ADRs found: No ADRs found. No recognised ADR directory (\u0060docs/adr/\u0060, \u0060docs/decisions/\u0060, \u0060adr/\u0060, \u0060docs/rfcs/\u0060, an \u0060ADR0001/\u0060 folder, or their siblings) exists anywhere in this tree. What was searched, so you can tell an empty log from a search that missed one: every directory under the tree (build output, dependencies and VCS metadata excepted), for a document that is either any non-index page inside a recognised ADR directory, whatever its name and however deeply nested (\u0060docs/adr/use-postgres.md\u0060, \u0060docs/adr/2024/0001-x.md\u0060); or a file anywhere whose name is ADR-shaped (\u00600001-use-postgres.md\u0060, \u0060adr-012-caching.md\u0060); or, when neither turned anything up, a document carrying the decision-record signature (an \u0022Architecture Decision Record\u0022 heading, or Status / Context / Decision / Consequences as section headings). A decision log that clears none of these \u2014 unnumbered files outside any recognised directory, without those headings \u2014 is not seen by this check and this row is then wrong. If that is your case, say so rather than renaming anything; otherwise, consider recording architectural decisions in \u0060docs/adr/\u0060."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"d2bea044ff79d7d275f5a91a6e2f548586178eaf480274c33960ad020c854631"}},{"ruleId":"D26","level":"note","message":{"text":"Projects may be oversized for their cohesion: 1 of 1 project(s) overshoot their size bounds, lowering Project Cohesion to 0.0/10. The most over is \u0060(repository root)\u0060 (113942 LoC, 68 public types across 21 directories). Review these for cohesion \u2014 draw the boundary inside the module first (group each responsibility into its own package or directory and keep the cross-boundary members non-public), since splitting a published package moves types between packages and breaks consumers."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"d5a94650dc74886a0f1f08eb9fb6775f1fc395279ef7e901c970c9d26f767a72"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"3c42b0ea8dddb18f7660b67dd9147c856078922c6b58bb16fff7a32101aeb302"},"properties":{"commitSha":"f9c1eb2323d4df6e97a9d72821a019f85a6aa628"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"8e4b1f49576e0a0ff66980b6ee216061492c4a644afae84ffca3400cdb0433bb"},"properties":{"commitSha":"f9c1eb2323d4df6e97a9d72821a019f85a6aa628"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"8d2b4c305120b03bfb8b7f65725017ae6db09581f7c1c645557521486b0e446b"},"properties":{"commitSha":"f30225cd95d0e5a27bd5ae20881f1fed900c41f2"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"4a53a3c82ab6cb1d9497ccf2ed25fb096bfd1ed7a1c66be382994c95ceb3ae09"},"properties":{"commitSha":"27996eeb3a022728fe1d99ef40fded78c7f25205"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"b4855a2f106b27183559b1685c715080484274bf19c34f342d48d24bc3035e0a"},"properties":{"commitSha":"358f9c28e58db73e2f3bfcdb1e04144ca2442850"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"ae9442537df544cb9ddded7a35354a06d22258ced3178b0fcf5e8752f3f78289"},"properties":{"commitSha":"5ba5e5b6c98c4d1a8ac31cb2c1999345973425da"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"bfa104a38ac6b29ee497ac95c1044899970df22e0ab8ebca53c586db3f931b98"},"properties":{"commitSha":"e95fc713080c0ebb99a4de27649fe92ef0e12d33"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"43cbebbf756d73fbba790773ccfa5a2005d8f1a635272dbf5be5c4f1a3c7e296"},"properties":{"commitSha":"f470529dd6b44936e77dd9e62dc4ce3b885a3cfc"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"3039c291d5521ddbc5bc9c302dfd8d5577a332502b4de3de5f17e8503e92952a"},"properties":{"commitSha":"f470529dd6b44936e77dd9e62dc4ce3b885a3cfc"}},{"ruleId":"D28","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"84eb5c73252643bdb291ce08d6be76631585d5f4117a9d3d72397cf2fb741972"},"properties":{"commitSha":"38dc381c83fb314d5bef3ddd901ed563487ad658"}},{"ruleId":"D28","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"3cdfd7beb31b791ae18dcb425e21eb157c842e7bbcd522cc330573c1a6538db1"}},{"ruleId":"D29","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"3c5e4ee71155c2fd21e1d22cd7624c9aba2dd4d2ce384c01b1ffde8652163a4e"},"taxa":[{"id":"CWE-1357","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-353","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D29","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"54da90ca50e76d8763c7ba905d6cb44e05adf09dc6f5f9100384868475f28840"},"taxa":[{"id":"CWE-1357","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-353","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D29","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"8b20cab276f8d49d9ec3ea3b1bfd48a96557b7208964671afe8f8e7a589c6248"},"taxa":[{"id":"CWE-1357","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-353","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D29","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"75e84d075c6b16491a51a80dda170641e6ccb19c8b4a5b5313a75e3ffc65dee4"},"taxa":[{"id":"CWE-522","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-829","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D29","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"24a26688069331274b85920130cc1592b1509aa9ff1ba1d1a1b23e79849fdf65"},"taxa":[{"id":"CWE-214","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-532","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D30","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"ffc10788097f7f4084333bc0631d079f010208230de17d39a7ce92e3934e5b34"},"properties":{"dependency":{"package":"nokogiri","version":"1.13.6","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]"],"reachability":{"kind":"unknown"}}}},{"ruleId":"D30","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"cdd0a7ecc16332535f869a4c0d6fabd5d6770567695cd122fe55c8e4fc586bae"},"properties":{"dependency":{"package":"rubyzip","version":"1.2.3","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[GHSA redacted]"],"reachability":{"kind":"unknown"}}}},{"ruleId":"D30","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"2671d2dc5243d2844e58ece392cfd92899b5067cc5e636d13daa8bfc4cef5229"},"properties":{"dependency":{"package":"jwt","version":"2.1.0","advisory":"[GHSA redacted]","aliases":["[CVE redacted]"],"reachability":{"kind":"unknown"}}}},{"ruleId":"D30","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"ae5226fc492343f6c867f4a7694ecefd62aee0166aee8371ac495d79873411c6"},"properties":{"dependency":{"package":"addressable","version":"2.8.0","advisory":"[GHSA redacted]","aliases":["[CVE redacted]"],"reachability":{"kind":"unknown"}}}},{"ruleId":"D30","level":"error","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"4704f6406bc0f7489643f63c801ed4c67dc13fba1b6c3afd96264ae7a985928a"},"properties":{"dependency":{"package":"json","version":"2.2.0","advisory":"[GHSA redacted]","aliases":["[CVE redacted]"],"reachability":{"kind":"unknown"}}}},{"ruleId":"D30","level":"warning","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"3acf461327c8d5eb139fb28055eb092d4d60f44d473a75aff035039585d5f410"},"properties":{"dependency":{"package":"excon","version":"0.65.0","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[GHSA redacted]"],"reachability":{"kind":"unknown"}}}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned files with no living knowledge: 17 of 331 analysed file(s) have no living knowledge left \u2014 their last meaningful change has decayed away, so if one breaks, no one currently understands it (counted over production source files of roughly 2,400 bytes or more, excluding vendored, generated and example/demo trees and test files identified by path convention, largest first; 331 of the 1872 production source files in this repository met that bar). None is large enough to earn a read-through of its own, so this row stands in for the per-file rows rather than raising one each \u2014 most significant first: HSTracker/UIs/StatsManager/LadderTab.swift, HSTracker/UIs/StatsManager/StatsTab.swift, HSTracker/HSReplay/LogUploader.swift, HSTracker/Logging/Parsers/TagChangeHandler.swift, scripts/hstracker_release.rb, HSTracker/Utility/Algorithm.swift, HSTracker/UIs/StatsManager/Statistics.swift, HSTracker/Logging/LogLine.swift (and 9 more). Attach the read to the next change that touches one of them: have a second person review that change, and leave behind a short comment or test recording what the file is for, so the knowledge comes back at the cost of a change you were making anyway."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"ce861fd78f6935114d3a342cb90ad25870f984e1042954fcbbe27c0e23be3658"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: ArenaHandler.swift \u2194 LoadingScreenHandler.swift: \u0060HSTracker/Logging/Handlers/ArenaHandler.swift\u0060 and \u0060HSTracker/Logging/Handlers/LoadingScreenHandler.swift\u0060 change together 67% of the time (10 of the 15 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in the same directory, and in this ecosystem sibling files there normally share one namespace/package \u2014 so a direct reference between them needs no import and this pass cannot see whether one exists. Read the pair before acting: if one file only DECLARES what the other consumes (a constants/types file beside its user), the co-change is definitional and the question is whether the split earns its keep; if they duplicate structure, extract the common part into a shared function or type they both call; if neither holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 10 shared commits counted here, the most recent 3 are \u0060a7f0db5b\u0060 Fix arena deck importing for underground and changes; \u00609141d38e\u0060 Remove unowned usage for weak, let instead; \u0060283d71d2\u0060 Remove Arena Helper \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Handlers/ArenaHandler.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"06d1c791a08b36f330229306f4eb34b8e877f3460ab198d21e6a07e1c40e8dfb"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: RootOverlayViewModel.swift \u2194 WindowManager.swift: \u0060HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift\u0060 and \u0060HSTracker/UIs/Trackers/WindowManager.swift\u0060 change together 56% of the time (9 of the 16 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 9 shared commits counted here, the most recent 3 are \u00605b1b7bae\u0060 Move the opponent hand markers onto the RootOverlay canvas; \u00604a92e825\u0060 Move the flavor text panel onto the RootOverlay canvas; \u0060e7583b34\u0060 Move the mulligan guide pre-lobby onto the RootOverlay canvas \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"95ca1d534404f306f7f91f6ac9aec24417a44e75b634198f4fe347d08ad843e0"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: BattlegroundsHeroPickingViewModel.swift \u2194 Tier7PreLobbyViewModel.swift: \u0060HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingViewModel.swift\u0060 and \u0060HSTracker/UIs/Battlegrounds/Tier7/Tier7PreLobbyViewModel.swift\u0060 change together 55% of the time (6 of the 11 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 6 shared commits counted here, the most recent 3 are \u0060f6eb03a9\u0060 feat(Battlegrounds): hero picking for Duos; \u00600ed584f1\u0060 feat: preparations for Battlegrounds Duos (incomplete); \u00609abe3a85\u0060 feat(Mulligan): attach star multiplier to requests \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Battlegrounds/HeroPicking/BattlegroundsHeroPickingViewModel.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"ab2531abcb47cebb54b3584159f6e45069f48f7943cd50fa48119b5928a02918"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: AppDelegate.swift \u2194 ArenaHandler.swift: \u0060HSTracker/AppDelegate.swift\u0060 and \u0060HSTracker/Logging/Handlers/ArenaHandler.swift\u0060 change together 53% of the time (8 of the 15 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 8 shared commits counted here, the most recent 3 are \u00609141d38e\u0060 Remove unowned usage for weak, let instead; \u0060283d71d2\u0060 Remove Arena Helper; \u0060ae88a473\u0060 Resolved several minor leaks \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1fc3cacd27b1cfd8eaee0b87dcda7bb772a9ef577860b7e548c65777bf827808"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: Game.swift \u2194 Http.swift: \u0060HSTracker/Logging/Game.swift\u0060 and \u0060HSTracker/Network/Http.swift\u0060 change together 53% of the time (8 of the 15 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 8 shared commits counted here, the most recent 3 are \u0060079fb872\u0060 Remove Wrap dependency; \u0060e49e7f03\u0060 Fix SwiftLint warnings; \u0060279cdbb1\u0060 Fix compilation for Xcode 12.2 Better handling of secrets for arena A\u2026 \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Game.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"0eef9e68d58e397538a8485180177ff89725efbe2a2937bc6a0999b151abda21"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: Player.swift \u2194 OpponentTrackersPreferences.swift: \u0060HSTracker/Logging/Player.swift\u0060 and \u0060HSTracker/UIs/Preferences/OpponentTrackersPreferences.swift\u0060 change together 53% of the time (8 of the 15 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 8 shared commits counted here, the most recent 3 are \u0060c2f33262\u0060 feat: add player max resources widget; \u0060e9ad6a07\u0060 feat: add Spell School counter; \u006049ab0ac9\u0060 The opponent deck list can now be set \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Logging/Player.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"2e4f50cdf13e9336edee74cd3b90e7c170cb8b876712a07d4dce89ebeed5aaa1"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: AppDelegate.swift \u2194 TurnTimer.swift: \u0060HSTracker/AppDelegate.swift\u0060 and \u0060HSTracker/Logging/TurnTimer.swift\u0060 change together 53% of the time (9 of the 17 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 9 shared commits counted here, the most recent 3 are \u0060683a02d0\u0060 Simplify the version checks that guarded the pre-10.15 fallback paths; \u00609141d38e\u0060 Remove unowned usage for weak, let instead; \u0060c9f68ebc\u0060 Fixed some more minor leaks \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/AppDelegate.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"fd055d2941131b4f28d4928e88ed096a36101a86d0fb45f6ba5d5d451ddb20ea"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: SizeHelper.swift \u2194 RootOverlayViewModel.swift: \u0060HSTracker/Core/SizeHelper.swift\u0060 and \u0060HSTracker/UIs/Overlay/Root/RootOverlayViewModel.swift\u0060 change together 50% of the time (8 of the 16 commits that touched whichever of the two files changed less often, counting a file under its earlier names as well \u2014 a repo-wide or module-wide sweep is evidence about the sweep rather than about any pair inside it and is left out of BOTH sides of this ratio, while a dependency bump, a formatter/rename sweep, or a commit whose edit to one of the two files was a tool directive such as //go:generate or whitespace only is left out of the shared count ONLY, so the two sides are not taken over identical commit sets). They sit in different directories, but in this ecosystem the namespace is declared in the FILE, not by the folder \u2014 so the two may well share one namespace and reference each other with no import for this pass to see. Read the pair before acting: if one derives from or overrides the other, the dependency is explicit in the type declaration and the co-change is definitional; if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE and the thing to add is a comment saying so; if they simply belong together, co-locate them; if none of these holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 8 shared commits counted here, the most recent 3 are \u00605b1b7bae\u0060 Move the opponent hand markers onto the RootOverlay canvas; \u00604a92e825\u0060 Move the flavor text panel onto the RootOverlay canvas; \u0060e7583b34\u0060 Move the mulligan guide pre-lobby onto the RootOverlay canvas \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Core/SizeHelper.swift"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"606b2ff6728011503f415d930fae8bfb1f2e3306ac41b81a3252e8ed6064cc13"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"1b213f6eedd4b140d0bc37bdf1496f72811a643f34064f12518b32e9e83bcfc7"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"0e17f71490e4d120a48b2b2881ab866c93b272bef2febb673a3e8e42b2c288ab"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"eb00976a698a5d68999b7ee6d27206fd374e916853e7ff1ead5eed42386f043f"}},{"ruleId":"D36","level":"warning","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"90f83b4fa27db32740afe9540c905f3c0499caed87f61049a8a903ecc95b41c3"}},{"ruleId":"D36","level":"warning","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"d657599f6f99da1927d3377533dfc0888b34c4d84aa7d5579841fd72d0e39bce"}},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"ace515990e7ee0f17b5c17e44dd168a5bdecf90804a3a3dd845cf05540978013"}},{"ruleId":"DM4","level":"error","message":{"text":"Anemic domain model: Deck: \u0060Deck\u0060 is a data-only reference entity whose behaviour is DELEGATED to a foreign utility (static methods that take it as their first parameter and hold its logic) \u2014 the anemic domain model. Move the behaviour onto the entity so it enforces its own invariants rather than being driven from outside."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Importers/DeckSerializer.swift"},"region":{"startLine":16}}}],"partialFingerprints":{"codehealthFindingId/v1":"7c903c2933b670befa8530b61611d5d712888bf2b6e449a5f8f210a6d1cc810e"}},{"ruleId":"DM4","level":"error","message":{"text":"Anemic domain model: Player: \u0060Player\u0060 is a data-only reference entity whose behaviour is DELEGATED to a foreign utility (static methods that take it as their first parameter and hold its logic) \u2014 the anemic domain model. Move the behaviour onto the entity so it enforces its own invariants rather than being driven from outside."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/HSReplay/Data/UploadMetaData.swift"},"region":{"startLine":367}}}],"partialFingerprints":{"codehealthFindingId/v1":"735c6702170c41bc53ace60e728b490f79830ae123f5226bfc2a70747972f0ed"}},{"ruleId":"M1","level":"warning","message":{"text":"README may be stale: 1266 code files changed in the last 6 months but the README was not touched \u2014 it may no longer reflect the system."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"e973e7a8d5d866995cbf78b120250fe7f132bf928812d48ca62e69aba11b9910"}},{"ruleId":"M2","level":"note","message":{"text":"No ADRs: No Architecture Decision Records found \u2014 no conventional ADR directory, no numbered \u0060NNNN-title\u0060 documents in any markup this check reads, and nothing ADR-shaped by content. Design rationale recorded elsewhere (a design-notes tree, a mailing list, pull-request discussion) is not visible to this check and is not re-findable per decision, so a future maintainer cannot ask why one choice was made and get an answer."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"670b3d6e36a756d63097d0dfbf90afd5fc761308800b9354894a07c3f4e4aa14"}},{"ruleId":"M2","level":"note","message":{"text":"No architecture diagram/doc: No C4/Structurizr/PlantUML/Mermaid/Graphviz/D2 diagram, no drawn diagram named for the architecture, no file named \u0060architecture\u0060 or \u0060design\u0060 in any markup this check reads, and nothing in the README, docs or contributor guides that announces the shape \u2014 no \u0060## Architecture\u0060 heading, no \u0022architecture overview\u0022/\u0022high-level design\u0022 phrasing, no \u0022the architecture is \u2026\u0022 introduction, no guided code tour. A shape laid out in prose that never names itself as the architecture is not visible to this check, and neither is one kept outside the repository, so this row reports the absence of a re-findable shape document \u2014 not evidence that nobody wrote the shape down."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"0c190e7c159d1850ee706c3ac4486e151e8a4fe169de4bf61436b9f399654f06"}},{"ruleId":"P1","level":"note","message":{"text":"CI build step not evidenced: A CI pipeline exists but no build step was matched \u2014 changes may merge without the build ever running. A build step may be invoked directly as a command, or declared as a task that a runner named in the pipeline resolves."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"251015acef1a259fcc13b7c60660cc8917ccb3ae8056cb5165ae03312dd586e8"}},{"ruleId":"P3","level":"note","message":{"text":"No SAST: No static application security testing detected. For this repository\u0027s stack, add CodeQL\u0027s Swift pack (Swift/Xcode) (or \u0060semgrep --config=auto\u0060, which runs on any language) as a CI step. What was searched, so you can tell an absence from a miss: the 1091 CI workflow file(s) in this repository, and the scanner and linter configuration checked in beside them. A scan that runs outside CI, one configured in your forge\u0027s web UI rather than in a committed file, or a tool whose name is none of those this check carries, is not seen \u2014 if that is your case the row is wrong, and saying so is more useful than adding a second scanner."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"6e54424179c892f03ef2fd003130ac4bd43f39bbf3b1ca0a0143e25acb80ec87"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060$0.type == .minion \u0026\u0026 $0.mechanics.contains(\u0022COLOSSAL\u0022)\u0060 appears character-identically in 2 files \u2014 HSTracker/Hearthstone/RelatedCardsSystem/Cards/Neutral/PrimordialLord.swift, HSTracker/Hearthstone/RelatedCardsSystem/Cards/Warrior/ClashOfTheColossals.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Neutral/PrimordialLord.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"8c143348fc53e099ba813ddbace546f9189667290fc0974e9e7db53b520cbfc3"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060$0.type == .spell \u0026\u0026 $0.mechanics.contains(\u0022SECRET\u0022)\u0060 appears character-identically in 2 files \u2014 HSTracker/Hearthstone/RelatedCardsSystem/Cards/Mage/DiscoAtTheEndOfTime.swift, HSTracker/Hearthstone/RelatedCardsSystem/Cards/Neutral/ObserverOfMysteries.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Mage/DiscoAtTheEndOfTime.swift"},"region":{"startLine":20}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1a49da0131fd42328c75ef550355dc921d3161ae2ab61ceab1ec0695067ef5f"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060$0.type == .spell \u0026\u0026 $0.mechanics.contains(\u0022SECRET\u0022) \u0026\u0026 $0.isClassOrNeutral(playerClass)\u0060 appears character-identically in 2 files \u2014 HSTracker/Hearthstone/RelatedCardsSystem/Cards/Hunter/PackKodo.swift, HSTracker/Hearthstone/RelatedCardsSystem/Cards/Pools/ClassOrNeutralSecretPool.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Hunter/PackKodo.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"82aadec0573372f901a58ddf803962d2962aa02a2c845f81c1e2da105b3687e3"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060card.mechanics.contains(\u0022SECRET\u0022) \u0026\u0026 card.isClass(cardClass: .mage) \u0026\u0026 card.isCardLegal(gameType: gameMode, format: format)\u0060 appears character-identically in 2 files \u2014 HSTracker/Hearthstone/RelatedCardsSystem/Cards/Mage/ArcaneKeysmith.swift, HSTracker/Hearthstone/RelatedCardsSystem/Cards/Pools/MageSecretPool.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Mage/ArcaneKeysmith.swift"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"c33d0ce7648dcf19b03a524118f48b4821dc13b268164cf6e71f96fe0d860ea2"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060card.mechanics.contains(\u0022SECRET\u0022) \u0026\u0026 card.isClass(cardClass: .paladin) \u0026\u0026 card.isCardLegal(gameType: gameMode, format: format)\u0060 appears character-identically in 2 files \u2014 HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/DesperateMeasures.swift, HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/Hydrologist.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/RelatedCardsSystem/Cards/Paladin/DesperateMeasures.swift"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"1ce42a8c8b0afca5391aedf1e204891729caec11b5dabb8b7d0501c8240a8616"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060card.rarity == .invalid \u0026\u0026 card.mechanics.contains(\u0022ELITE\u0022)\u0060 appears character-identically in 2 files \u2014 HSTracker/UIs/Cards/CardBar.swift, HSTracker/UIs/Overlay/Trackers/CardTileView.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/UIs/Cards/CardBar.swift"},"region":{"startLine":315}}}],"partialFingerprints":{"codehealthFindingId/v1":"784745623b6527c1a16ae5889683de3a7ddb8b9710c6792c9a65912a409ff399"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060tag == .zone \u0026\u0026 AppDelegate.instance().coreManager.logReaderManager.powerGameStateParser.currentBlock?.type == \u0022PLAY\u0022\u0060 appears character-identically in 2 files \u2014 HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift, HSTracker/Hearthstone/CounterSystem/Counters/PogoHopperCounter.swift. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"HSTracker/Hearthstone/CounterSystem/Counters/ChaoticTendrilCounter.swift"},"region":{"startLine":47}}}],"partialFingerprints":{"codehealthFindingId/v1":"ed6c41e8efac1f189774d83fa78b4f1d6c9aaddacb08d3960df2919acd39e8f9"}}],"taxonomies":[{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d","organization":"MITRE","informationUri":"https://cwe.mitre.org/","isComprehensive":false,"shortDescription":{"text":"The MITRE Common Weakness Enumeration (CWE)."},"taxa":[{"id":"CWE-1357","guid":"e4d2e772-757e-0a5c-bd7d-77052949d866","name":"Reliance on Insufficiently Trustworthy Component","shortDescription":{"text":"Reliance on Insufficiently Trustworthy Component"},"helpUri":"https://cwe.mitre.org/data/definitions/1357.html"},{"id":"CWE-1395","guid":"800e09e7-c11a-8654-9fa6-86f398995fed","name":"Dependency on Vulnerable Third-Party Component","shortDescription":{"text":"Dependency on Vulnerable Third-Party Component"},"helpUri":"https://cwe.mitre.org/data/definitions/1395.html"},{"id":"CWE-214","guid":"b10ad120-fb22-1351-9348-aa23b453e815","name":"CWE-214","shortDescription":{"text":"CWE-214"},"helpUri":"https://cwe.mitre.org/data/definitions/214.html"},{"id":"CWE-259","guid":"ae9ad959-fbb6-9d5e-892d-3dca66da0b69","name":"Use of Hard-coded Password","shortDescription":{"text":"Use of Hard-coded Password"},"helpUri":"https://cwe.mitre.org/data/definitions/259.html"},{"id":"CWE-353","guid":"09d7e902-d4ee-f05d-ae6c-0a1554d0c18f","name":"CWE-353","shortDescription":{"text":"CWE-353"},"helpUri":"https://cwe.mitre.org/data/definitions/353.html"},{"id":"CWE-494","guid":"b8a65e0d-e459-4a55-a931-fc1136482375","name":"Download of Code Without Integrity Check","shortDescription":{"text":"Download of Code Without Integrity Check"},"helpUri":"https://cwe.mitre.org/data/definitions/494.html"},{"id":"CWE-506","guid":"401d6455-56e3-0552-9a39-f77461673e3f","name":"CWE-506","shortDescription":{"text":"CWE-506"},"helpUri":"https://cwe.mitre.org/data/definitions/506.html"},{"id":"CWE-522","guid":"71fb233e-ce6a-ae57-9419-ef8373540b09","name":"CWE-522","shortDescription":{"text":"CWE-522"},"helpUri":"https://cwe.mitre.org/data/definitions/522.html"},{"id":"CWE-532","guid":"1cd8877a-76e8-ce54-b40b-779af23364a0","name":"CWE-532","shortDescription":{"text":"CWE-532"},"helpUri":"https://cwe.mitre.org/data/definitions/532.html"},{"id":"CWE-77","guid":"332c8ade-6612-9f56-a06b-d8d90b1a8750","name":"Command Injection","shortDescription":{"text":"Command Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/77.html"},{"id":"CWE-78","guid":"2e31ceaf-c7ae-2e5e-9661-cfb1362789cf","name":"OS Command Injection","shortDescription":{"text":"OS Command Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/78.html"},{"id":"CWE-79","guid":"fd45580b-e8c4-fc5e-8c2f-aa8fab0b4dbf","name":"Cross-site Scripting (XSS)","shortDescription":{"text":"Cross-site Scripting (XSS)"},"helpUri":"https://cwe.mitre.org/data/definitions/79.html"},{"id":"CWE-798","guid":"5e8f057d-fee3-995a-a0cb-9fc5b0d174d1","name":"Use of Hard-coded Credentials","shortDescription":{"text":"Use of Hard-coded Credentials"},"helpUri":"https://cwe.mitre.org/data/definitions/798.html"},{"id":"CWE-829","guid":"13c33925-97fb-5a5e-b40c-56d328b8a4d7","name":"CWE-829","shortDescription":{"text":"CWE-829"},"helpUri":"https://cwe.mitre.org/data/definitions/829.html"},{"id":"CWE-89","guid":"6d08fdad-37eb-c150-bbf0-d7d946863407","name":"SQL Injection","shortDescription":{"text":"SQL Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/89.html"},{"id":"CWE-937","guid":"16f316ae-415c-b354-a59b-1f7905f756e9","name":"Using Components with Known Vulnerabilities","shortDescription":{"text":"Using Components with Known Vulnerabilities"},"helpUri":"https://cwe.mitre.org/data/definitions/937.html"},{"id":"CWE-94","guid":"75e7f50c-6c2f-dd52-bf40-bf6c52b861fd","name":"Code Injection","shortDescription":{"text":"Code Injection"},"helpUri":"https://cwe.mitre.org/data/definitions/94.html"}]}],"properties":{"codehealthPublication":{"public":true,"notice":"This is the PUBLIC form of this artifact. Findings are listed in full, but the details of SECURITY findings \u2014 which rule fired, in which file, on which line, and how to fix it \u2014 are deliberately withheld, and any secret-scanner results are excluded entirely. Where detail is absent here it was REMOVED FOR PUBLICATION; it is not missing from the analysis. The complete artifact is available from the repository owner.","securityFindingsRedacted":30,"secretScannerRunsExcluded":0}},"redactionTokens":["A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."]}]}