{"$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":"D5","name":"Coupling","shortDescription":{"text":"Coupling"},"helpUri":"https://codehealth.canine.dev/dimensions/D5"},{"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":"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":"D15","name":"Churn \u00D7 Complexity Hotspots","shortDescription":{"text":"Churn \u00D7 Complexity Hotspots"},"helpUri":"https://codehealth.canine.dev/dimensions/D15"},{"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":"D22","name":"Internal API Consistency","shortDescription":{"text":"Internal API Consistency"},"helpUri":"https://codehealth.canine.dev/dimensions/D22"},{"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":"AC1","name":"Text alternatives","shortDescription":{"text":"Text alternatives"},"helpUri":"https://codehealth.canine.dev/dimensions/AC1"},{"id":"AC2","name":"Forms \u0026 labels","shortDescription":{"text":"Forms \u0026 labels"},"helpUri":"https://codehealth.canine.dev/dimensions/AC2"},{"id":"AC3","name":"Page structure","shortDescription":{"text":"Page structure"},"helpUri":"https://codehealth.canine.dev/dimensions/AC3"},{"id":"AC4","name":"Keyboard semantics","shortDescription":{"text":"Keyboard semantics"},"helpUri":"https://codehealth.canine.dev/dimensions/AC4"},{"id":"AC5","name":"ARIA correctness","shortDescription":{"text":"ARIA correctness"},"helpUri":"https://codehealth.canine.dev/dimensions/AC5"},{"id":"AC6","name":"Visual \u0026 motion safety","shortDescription":{"text":"Visual \u0026 motion safety"},"helpUri":"https://codehealth.canine.dev/dimensions/AC6"},{"id":"AC7","name":"A11y enforcement","shortDescription":{"text":"A11y enforcement"},"helpUri":"https://codehealth.canine.dev/dimensions/AC7"},{"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":"AX9","name":"CQS / query purity","shortDescription":{"text":"CQS / query purity"},"helpUri":"https://codehealth.canine.dev/dimensions/AX9"},{"id":"AXB2","name":"Runtime readiness","shortDescription":{"text":"Runtime readiness"},"helpUri":"https://codehealth.canine.dev/dimensions/AXB2"},{"id":"ES1","name":"Fold determinism","shortDescription":{"text":"Fold determinism"},"helpUri":"https://codehealth.canine.dev/dimensions/ES1"},{"id":"ES2","name":"Immutable events","shortDescription":{"text":"Immutable events"},"helpUri":"https://codehealth.canine.dev/dimensions/ES2"},{"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":"P10","name":"Library API \u0026 versioning","shortDescription":{"text":"Library API \u0026 versioning"},"helpUri":"https://codehealth.canine.dev/dimensions/P10"},{"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":"PF1","name":"Benchmark discipline","shortDescription":{"text":"Benchmark discipline"},"helpUri":"https://codehealth.canine.dev/dimensions/PF1"},{"id":"PF3","name":"Async \u0026 latency hygiene","shortDescription":{"text":"Async \u0026 latency hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/PF3"},{"id":"R1","name":"Type Safety","shortDescription":{"text":"Type Safety"},"helpUri":"https://codehealth.canine.dev/dimensions/R1"},{"id":"R10","name":"Code Duplication","shortDescription":{"text":"Code Duplication"},"helpUri":"https://codehealth.canine.dev/dimensions/R10"},{"id":"R2","name":"Cyclomatic Complexity","shortDescription":{"text":"Cyclomatic Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/R2"},{"id":"R3","name":"Large Files","shortDescription":{"text":"Large Files"},"helpUri":"https://codehealth.canine.dev/dimensions/R3"},{"id":"R4","name":"Test Coverage","shortDescription":{"text":"Test Coverage"},"helpUri":"https://codehealth.canine.dev/dimensions/R4"},{"id":"R6","name":"Tooling","shortDescription":{"text":"Tooling"},"helpUri":"https://codehealth.canine.dev/dimensions/R6"},{"id":"R7","name":"Dead Code","shortDescription":{"text":"Dead Code"},"helpUri":"https://codehealth.canine.dev/dimensions/R7"},{"id":"R8","name":"Dependency Hygiene","shortDescription":{"text":"Dependency Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/R8"},{"id":"R9","name":"Circular Imports","shortDescription":{"text":"Circular Imports"},"helpUri":"https://codehealth.canine.dev/dimensions/R9"},{"id":"SC1","name":"Supply-chain hygiene","shortDescription":{"text":"Supply-chain hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/SC1"},{"id":"X10","name":"Duplicated predicate","shortDescription":{"text":"Duplicated predicate"},"helpUri":"https://codehealth.canine.dev/dimensions/X10"},{"id":"X6","name":"Hand-rolled structured-format parsing","shortDescription":{"text":"Hand-rolled structured-format parsing"},"helpUri":"https://codehealth.canine.dev/dimensions/X6"},{"id":"X7","name":"Silent fallback defaults","shortDescription":{"text":"Silent fallback defaults"},"helpUri":"https://codehealth.canine.dev/dimensions/X7"},{"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":"CanvasWorkspace.CanvasWorkspace (cyclomatic 141): CanvasWorkspace.CanvasWorkspace has cyclomatic complexity 141 (threshold 15). Most of this is not in the body itself: 15 of the 141 points are its own statements and the rest belongs to 24 function literals inside it that branch (lines 542, 320, 465, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"ebcf86c18c40341d2cd30816a849c8715105e2bef6ad0e7d87cc8fb378210c0a"}},{"ruleId":"D1","level":"warning","message":{"text":"TextLayout::run_with_size (cyclomatic 58): TextLayout::run_with_size has cyclomatic complexity 58 (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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":463}}}],"partialFingerprints":{"codehealthFindingId/v1":"3003e473be9d56c1fbc54add2c8b0d90f98b9c900cb29c174d83b13c5dbcbbc5"}},{"ruleId":"D1","level":"warning","message":{"text":"PageRail.PageRail (cyclomatic 44): PageRail.PageRail has cyclomatic complexity 44 (threshold 15). Most of this is not in the body itself: 5 of the 44 points are its own statements and the rest belongs to 15 function literals inside it that branch (lines 115, 167, 138, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/PageRail.tsx"},"region":{"startLine":69}}}],"partialFingerprints":{"codehealthFindingId/v1":"7009ebf71f0a829d97ccfdb5ab3effc47da2a2a7f2cb3e6a6fa48b0c8060c901"}},{"ruleId":"D1","level":"warning","message":{"text":"Inspector.LayersInspector (cyclomatic 37): Inspector.LayersInspector has cyclomatic complexity 37 (threshold 15). Most of this is not in the body itself: 4 of the 37 points are its own statements and the rest belongs to 11 function literals inside it that branch (lines 552, 598, 512, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":493}}}],"partialFingerprints":{"codehealthFindingId/v1":"43acbd28f165c93997d2f5b765ae2ff326a856f0499590dbdf228d521d4b7ad3"}},{"ruleId":"D1","level":"warning","message":{"text":"AgentPanel.AgentPanel (cyclomatic 36): AgentPanel.AgentPanel has cyclomatic complexity 36 (threshold 15). Most of this is not in the body itself: 14 of the 36 points are its own statements and the rest belongs to 7 function literals inside it that branch (lines 293, 120, 170, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"6e274378b056e8c0b84d3c6f804536fde4180c2cfbf746062742fbe6706c9e7f"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_scene::schema::validate_entity (cyclomatic 34): koharu_scene::schema::validate_entity has cyclomatic complexity 34 (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":"crates/koharu-scene/src/schema.rs"},"region":{"startLine":75}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd1e3f42cb713e63594d518675496c3ae06e444f8911c6a20a130224e660490b"}},{"ruleId":"D1","level":"warning","message":{"text":"SettingsPage.SettingsPage (cyclomatic 34): SettingsPage.SettingsPage has cyclomatic complexity 34 (threshold 15). Most of this is not in the body itself: 15 of the 34 points are its own statements and the rest belongs to 6 function literals inside it that branch (lines 83, 144, 163, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/SettingsPage.tsx"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"64b7c6a0d1f4d9e4788dfb8671e142e5437fb6fe42be11a0c1e7795db1efeb2a"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_ml::comic_text_detector::processor::rearranged_inference (cyclomatic 33): koharu_ml::comic_text_detector::processor::rearranged_inference has cyclomatic complexity 33 (threshold 15). 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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":114}}}],"partialFingerprints":{"codehealthFindingId/v1":"589ebb1853ade6daaf8dbde71e7c0247a43c24b29c8182121bff29b04333f66c"}},{"ruleId":"D1","level":"warning","message":{"text":"Header::parse (cyclomatic 32): Header::parse has cyclomatic complexity 32 (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":"crates/koharu-torch/src/tensor/npy.rs"},"region":{"startLine":80}}}],"partialFingerprints":{"codehealthFindingId/v1":"a508804f688d177de7bc423542a54b4cbc74b8f3bfb2fc63596287f6585f9c41"}},{"ruleId":"D1","level":"warning","message":{"text":"Operation::apply (cyclomatic 28): Operation::apply 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":"crates/koharu-scene/src/patch.rs"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"17050f9abbac137badd58b787c5d633276eb69bdb581e7b47d88df37aecd5571"}},{"ruleId":"D1","level":"warning","message":{"text":"Updater.Updater (cyclomatic 27): Updater.Updater has cyclomatic complexity 27 (threshold 15). Of this number, 18 points are the body\u0027s own statements and 9 belong to 5 function literals inside it that branch. 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":"packages/koharu/components/app/Updater.tsx"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"2b84d856d50f96f45eff8c64129e9fb45cd14ab070fcad92dec4536e562eecee"}},{"ruleId":"D1","level":"warning","message":{"text":"ModelPicker.ModelPicker (cyclomatic 27): ModelPicker.ModelPicker has cyclomatic complexity 27 (threshold 15). Most of this is not in the body itself: 12 of the 27 points are its own statements and the rest belongs to 4 function literals inside it that branch (lines 112, 47, 65, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d6091662f88179fede80790dd7c9e3d32fc900c088aa5bee44392b365ab019c"}},{"ruleId":"D1","level":"warning","message":{"text":"InferenceControl.RuntimeSelector (cyclomatic 26): InferenceControl.RuntimeSelector has cyclomatic complexity 26 (threshold 15). Of this number, 13 points are the body\u0027s own statements and 13 belong to 5 function literals inside it that branch. 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":"packages/koharu/components/editor/InferenceControl.tsx"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"2399ad4bf16465ab42d79e8892c716e37de7e21ae2baa4cb827eb67294ecfbcf"}},{"ruleId":"D1","level":"warning","message":{"text":"Inspector.TypeInspector (cyclomatic 25): Inspector.TypeInspector has cyclomatic complexity 25 (threshold 15). Most of this is not in the body itself: 12 of the 25 points are its own statements and the rest belongs to 7 function literals inside it that branch (lines 149, 385, 414, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":131}}}],"partialFingerprints":{"codehealthFindingId/v1":"af14a817a93bd57f0e08727299314c1420c3ca0bc52828bb8d9ba10633a1c714"}},{"ruleId":"D1","level":"warning","message":{"text":"useCanvas.useCanvas (cyclomatic 24): useCanvas.useCanvas has cyclomatic complexity 24 (threshold 15). Most of this is not in the body itself: 1 of the 24 points is its own statement and the rest belongs to 11 function literals inside it that branch (lines 95, 58, 130, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/useCanvas.ts"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"859bad69858c4bdc0a3149067c81ee156ccaac317746afcb7154eb84f9d4def7"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_agent::codex::stream::read (cyclomatic 23): koharu_agent::codex::stream::read 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":"crates/koharu-agent/src/codex/stream.rs"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"9b8bffda1b72e49c65feb7dfdc718e0865ad493eb4702f93edcbc056adbb0d2e"}},{"ruleId":"D1","level":"warning","message":{"text":"SelectionControls.SelectionControls (cyclomatic 23): SelectionControls.SelectionControls has cyclomatic complexity 23 (threshold 15). Most of this is not in the body itself: 1 of the 23 points is its own statement and the rest belongs to 7 function literals inside it that branch (lines 144, 86, 77, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/SelectionControls.tsx"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"d239162683701f0ce588b85bdaa9f6d47b5836e1a6191cc3dc5d0379cf9d93a9"}},{"ruleId":"D1","level":"warning","message":{"text":"Tensor::f_indexer (cyclomatic 22): Tensor::f_indexer has cyclomatic complexity 22 (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":"crates/koharu-torch/src/tensor/index.rs"},"region":{"startLine":293}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d976ad848681c57d8779bc90efef102dcfc05d3d53027e8886db327b4d84f4e"}},{"ruleId":"D1","level":"warning","message":{"text":"chart.ChartTooltipContent (cyclomatic 22): chart.ChartTooltipContent has cyclomatic complexity 22 (threshold 15). Most of this is not in the body itself: 5 of the 22 points are its own statements and the rest belongs to 2 function literals inside it that branch (lines 202, 149). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/chart.tsx"},"region":{"startLine":119}}}],"partialFingerprints":{"codehealthFindingId/v1":"bdbac9f0273230293b719545a5d7de8be32da0d8e5d214b8c0d691f7e09ce92f"}},{"ruleId":"D1","level":"warning","message":{"text":"FontPicker.FontPicker (cyclomatic 21): FontPicker.FontPicker has cyclomatic complexity 21 (threshold 15). Most of this is not in the body itself: 8 of the 21 points are its own statements and the rest belongs to 3 function literals inside it that branch (lines 87, 75, 116). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/FontPicker.tsx"},"region":{"startLine":50}}}],"partialFingerprints":{"codehealthFindingId/v1":"0e84e22df692d75e9e7ef70811f96083b2ba4e7f1b5d62efb1cb661fd1dc34a8"}},{"ruleId":"D1","level":"warning","message":{"text":"CanvasOverlay.CanvasOverlay (cyclomatic 21): CanvasOverlay.CanvasOverlay has cyclomatic complexity 21 (threshold 15). Of this number, 11 points are the body\u0027s own statements and 10 belong to 3 function literals inside it that branch. 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":"packages/koharu/components/editor/CanvasOverlay.tsx"},"region":{"startLine":33}}}],"partialFingerprints":{"codehealthFindingId/v1":"5d47f4ed2e0ce3e94003490652fc165e2d6fda8a9281cabecb93337105808e99"}},{"ruleId":"D1","level":"warning","message":{"text":"geometry.layerFrame (cyclomatic 21): geometry.layerFrame 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":"packages/koharu/lib/geometry.ts"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"f2f030998e7328337cf2f38afe02565d0157ca2ae373ccca2164712db6fb4d88"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_psd::document::build (cyclomatic 20): koharu_psd::document::build has cyclomatic complexity 20 (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":"crates/koharu-psd/src/document.rs"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"0f3b439d7a61f2a0deddd8c0f1b8e502db52714a8446179989af5b7fcc7fc128"}},{"ruleId":"D1","level":"warning","message":{"text":"GpuCompositor::render (cyclomatic 19): GpuCompositor::render has cyclomatic complexity 19 (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":"crates/koharu-rasterizer/src/compositor.rs"},"region":{"startLine":389}}}],"partialFingerprints":{"codehealthFindingId/v1":"e6f46a837665ee788fd54f9d2f28402699431bbeee76b5989b3f8a7fd83ec14c"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_renderer::layout::optimal_uniform_line_breaks (cyclomatic 19): koharu_renderer::layout::optimal_uniform_line_breaks 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1430}}}],"partialFingerprints":{"codehealthFindingId/v1":"470eb103f998a9aefcff9ad99bd9c8a93d26042f5acc191edeb439bbae0e8914"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_renderer::layout::exact_profiled_line_breaks (cyclomatic 19): koharu_renderer::layout::exact_profiled_line_breaks 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1632}}}],"partialFingerprints":{"codehealthFindingId/v1":"293eebb0d7378f19815ffe4b48014b82e881c2cb83f1204f27cf290bb87d1447"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_renderer::bubble::decompose_lobes (cyclomatic 19): koharu_renderer::bubble::decompose_lobes 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":"crates/koharu-renderer/src/bubble.rs"},"region":{"startLine":116}}}],"partialFingerprints":{"codehealthFindingId/v1":"d94df241505ea7f48bf284c9cdea5c036a12ca08b650ffec4a07fdba57e2ea57"}},{"ruleId":"D1","level":"warning","message":{"text":"TextRenderer::render_descriptor (cyclomatic 18): TextRenderer::render_descriptor 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":"crates/koharu-renderer/src/text_renderer.rs"},"region":{"startLine":148}}}],"partialFingerprints":{"codehealthFindingId/v1":"c5d91c2a6d3a1f2fb884d0464487bba5325d3d90ad87f6b314f84612951bc715"}},{"ruleId":"D1","level":"warning","message":{"text":"Edit::remove_entity (cyclomatic 18): Edit::remove_entity has cyclomatic complexity 18 (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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":202}}}],"partialFingerprints":{"codehealthFindingId/v1":"ea327df193467a0c03f8e59edc5896fdfb1b36470fb8520a1e8b7bc96e44839b"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_runtime::hardware::hip::linux::probe (cyclomatic 17): koharu_runtime::hardware::hip::linux::probe 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":"crates/koharu-runtime/src/hardware/hip/linux.rs"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"b7edecfbc906851c089296f3933b9838a4b385d464debdaacf78ed69221133da"}},{"ruleId":"D1","level":"warning","message":{"text":"TensorFormatter::fmt_tensor (cyclomatic 17): TensorFormatter::fmt_tensor 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":"crates/koharu-torch/src/tensor/display.rs"},"region":{"startLine":171}}}],"partialFingerprints":{"codehealthFindingId/v1":"42e84ce99f242f68986a5131b59ed7783b867974a1e9e8d9d346be4f34f72056"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_ml::comic_text_detector::processor::connected_components (cyclomatic 16): koharu_ml::comic_text_detector::processor::connected_components 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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":1128}}}],"partialFingerprints":{"codehealthFindingId/v1":"db2a334c0f93a1e229955f5618d07bf830da37563857addd90323c4c3d038cc7"}},{"ruleId":"D1","level":"warning","message":{"text":"Model::run (cyclomatic 16): Model::run 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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":177}}}],"partialFingerprints":{"codehealthFindingId/v1":"5fdd349c7e578c95d9818a32fc6896b970061cc27db440156204e9ad17fd4818"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_pipeline::stages::detection::infer_outline (cyclomatic 16): koharu_pipeline::stages::detection::infer_outline 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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":760}}}],"partialFingerprints":{"codehealthFindingId/v1":"626220c2a288f1ef87c5c1ec450bedb74ea82ebd41b6811ead4463be0f739fcb"}},{"ruleId":"D1","level":"warning","message":{"text":"koharu_pipeline::stages::inpainting::prepare (cyclomatic 16): koharu_pipeline::stages::inpainting::prepare 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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":430}}}],"partialFingerprints":{"codehealthFindingId/v1":"c90569ea3a0554dcc5bf84acb1395c05b2fb3939738ff01fa5052779746459f5"}},{"ruleId":"D1","level":"warning","message":{"text":"TextLayout::append_balanced_segment_lines (cyclomatic 16): TextLayout::append_balanced_segment_lines 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":955}}}],"partialFingerprints":{"codehealthFindingId/v1":"21db296b3b2485f106bb0393fac48907a99f30f3593baff876a61aa5d6e47c38"}},{"ruleId":"D2","level":"warning","message":{"text":"TextLayout::run_with_size (cognitive 157): TextLayout::run_with_size has cognitive complexity 157 (threshold 15). Drivers by points: if/else 55 (112 pts), loops 12 (33 pts), boolean chains 6, match/switch 2 (6 pts) (nesting depth added 82). 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":463}}}],"partialFingerprints":{"codehealthFindingId/v1":"643f813d305c923f85d3f4d1d56168db00c41484df11b512dba36ed761592eeb"}},{"ruleId":"D2","level":"warning","message":{"text":"CanvasWorkspace.CanvasWorkspace (cognitive 147): CanvasWorkspace.CanvasWorkspace has cognitive complexity 147 (threshold 15). Drivers by points: if/else 61 (73 pts), boolean chains 34, ternaries 16 (28 pts), error handling 6 (10 pts), loops 1 (2 pts) (nesting depth added 29). Most of this is not in the body itself: 10 of the 147 points are its own statements and the rest belongs to 24 function literals inside it that branch (lines 542, 320, 465, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"d31c9e1d181c8c68c5608fb54f64e6efba98d47d1d1e992eea86c1f3b4b0c272"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::comic_text_detector::processor::rearranged_inference (cognitive 99): koharu_ml::comic_text_detector::processor::rearranged_inference has cognitive complexity 99 (threshold 15). Drivers by points: loops 17 (58 pts), if/else 20 (39 pts), boolean chains 2 (nesting depth added 60). 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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":114}}}],"partialFingerprints":{"codehealthFindingId/v1":"409dcb2a0d34f9e6b0052a4400eaf0dc200bfc7735b13ef6324d5f797fb4cfb2"}},{"ruleId":"D2","level":"warning","message":{"text":"PageRail.PageRail (cognitive 46): PageRail.PageRail has cognitive complexity 46 (threshold 15). Drivers by points: if/else 22 (26 pts), boolean chains 14, ternaries 3 (6 pts) (nesting depth added 7). Most of this is not in the body itself: 5 of the 46 points are its own statements and the rest belongs to 15 function literals inside it that branch (lines 115, 167, 138, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/PageRail.tsx"},"region":{"startLine":69}}}],"partialFingerprints":{"codehealthFindingId/v1":"4b67d6707d8697e03c6f796f4b4249c6e0e59b57f54cffa372756286c73b8fe7"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::comic_text_detector::processor::connected_components (cognitive 42): koharu_ml::comic_text_detector::processor::connected_components has cognitive complexity 42 (threshold 15). Drivers by points: if/else 4 (21 pts), loops 5 (15 pts), boolean chains 6 (nesting depth added 27). 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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":1128}}}],"partialFingerprints":{"codehealthFindingId/v1":"6cc20c0c4d1af6e42a030353502794164e7c6e147906254660741e911eaca9bd"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::layout::exact_profiled_line_breaks (cognitive 42): koharu_renderer::layout::exact_profiled_line_breaks has cognitive complexity 42 (threshold 15). Drivers by points: if/else 11 (31 pts), loops 4 (7 pts), boolean chains 4 (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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1632}}}],"partialFingerprints":{"codehealthFindingId/v1":"32edb83ebfbdbfd8119b1dd4c595d4415a256ac58d7f87bca3affd1db2897fa7"}},{"ruleId":"D2","level":"warning","message":{"text":"TensorFormatter::fmt_tensor (cognitive 37): TensorFormatter::fmt_tensor has cognitive complexity 37 (threshold 15). Drivers by points: if/else 7 (19 pts), loops 6 (15 pts), boolean chains 2, 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":"crates/koharu-torch/src/tensor/display.rs"},"region":{"startLine":171}}}],"partialFingerprints":{"codehealthFindingId/v1":"773eec8dc4495a1e43b7629626846d6e64ac4824fd1a1caa1c7a86f7cfabcd78"}},{"ruleId":"D2","level":"warning","message":{"text":"Inspector.LayersInspector (cognitive 37): Inspector.LayersInspector has cognitive complexity 37 (threshold 15). Drivers by points: boolean chains 14, ternaries 11 (14 pts), if/else 8 (9 pts) (nesting depth added 4). Most of this is not in the body itself: 3 of the 37 points are its own statements and the rest belongs to 11 function literals inside it that branch (lines 552, 512, 598, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":493}}}],"partialFingerprints":{"codehealthFindingId/v1":"683cf880f7a4dc460473e6a1b45a920ddeecc93e5035a3a0a7998eff49f76e3d"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::inpainting::prepare (cognitive 36): koharu_pipeline::stages::inpainting::prepare has cognitive complexity 36 (threshold 15). Drivers by points: if/else 15 (27 pts), loops 3 (8 pts), boolean chains 1 (nesting depth added 17). 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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":430}}}],"partialFingerprints":{"codehealthFindingId/v1":"dc36cdeaddf46d471d12555aae5674199874c53cd77575b365df8693f224c630"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_scene::schema::validate_entity (cognitive 36): koharu_scene::schema::validate_entity has cognitive complexity 36 (threshold 15). Drivers by points: boolean chains 18, if/else 16 (18 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":"crates/koharu-scene/src/schema.rs"},"region":{"startLine":75}}}],"partialFingerprints":{"codehealthFindingId/v1":"fbec0cf19cd22cb15e3b393a46aad7c97c2d197899728e4753e0dc0fdbe28894"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::layout::optimal_uniform_line_breaks (cognitive 34): koharu_renderer::layout::optimal_uniform_line_breaks has cognitive complexity 34 (threshold 15). Drivers by points: if/else 12 (26 pts), boolean chains 4, loops 3 (4 pts) (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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1430}}}],"partialFingerprints":{"codehealthFindingId/v1":"d6a7f9cbb8ff4db9e89ab1abd5140d1e9d6a9193ee2dbd80dca082a3fdf83582"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::bubble::decompose_lobes (cognitive 33): koharu_renderer::bubble::decompose_lobes has cognitive complexity 33 (threshold 15). Drivers by points: if/else 10 (20 pts), loops 4 (7 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":"crates/koharu-renderer/src/bubble.rs"},"region":{"startLine":116}}}],"partialFingerprints":{"codehealthFindingId/v1":"13b9866706f5985d30692fae5756e999df293178ce62d76bf8649dc84c63e20e"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::bin::koharu_layout_rfdetr_seg_2xl::draw_detections (cognitive 32): koharu_ml::bin::koharu_layout_rfdetr_seg_2xl::draw_detections has cognitive complexity 32 (threshold 15). Drivers by points: if/else 7 (19 pts), loops 6 (12 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":"crates/koharu-ml/src/bin/koharu_layout_rfdetr_seg_2xl.rs"},"region":{"startLine":98}}}],"partialFingerprints":{"codehealthFindingId/v1":"ddeaff1a4f1d629f1873441d6c7b3d6627802beebee19fe2e1d600e113ff3b83"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::detection::infer_outline (cognitive 32): koharu_pipeline::stages::detection::infer_outline has cognitive complexity 32 (threshold 15). Drivers by points: if/else 8 (23 pts), loops 4 (6 pts), boolean chains 3 (nesting depth added 17). 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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":760}}}],"partialFingerprints":{"codehealthFindingId/v1":"a8a729f6c0c347d3b439a1995b65ca9417a99db28e2db2a39a7af2747c64988e"}},{"ruleId":"D2","level":"warning","message":{"text":"TextLayout::shaped_block_extents (cognitive 32): TextLayout::shaped_block_extents has cognitive complexity 32 (threshold 15). Drivers by points: if/else 6 (22 pts), loops 3 (6 pts), match/switch 1 (4 pts) (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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":896}}}],"partialFingerprints":{"codehealthFindingId/v1":"856301ad0a9b8d249ef3505b820387df011d925598ca6d1c22ee8a49862118df"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::renderer::resolve_balloon_flows (cognitive 32): koharu_renderer::renderer::resolve_balloon_flows has cognitive complexity 32 (threshold 15). Drivers by points: if/else 10 (25 pts), loops 4 (7 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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":919}}}],"partialFingerprints":{"codehealthFindingId/v1":"2464f362802e876be4b77a52512239fd7733745c1f396334789929ac945c2413"}},{"ruleId":"D2","level":"warning","message":{"text":"TextLayout::layout_cjk_emphasis_runs (cognitive 31): TextLayout::layout_cjk_emphasis_runs has cognitive complexity 31 (threshold 15). Drivers by points: if/else 9 (19 pts), loops 4 (9 pts), match/switch 1 (2 pts), boolean chains 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1240}}}],"partialFingerprints":{"codehealthFindingId/v1":"ae47f50ae9b2abff517360a50eb81b24ec0a199fdfb01d52dc103686e6cc3830"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::bubble::anchor_flow_cells (cognitive 31): koharu_renderer::bubble::anchor_flow_cells has cognitive complexity 31 (threshold 15). Drivers by points: if/else 13 (25 pts), loops 4 (6 pts) (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":"crates/koharu-renderer/src/bubble.rs"},"region":{"startLine":373}}}],"partialFingerprints":{"codehealthFindingId/v1":"383a0157cfba614852e63655acc0576ea0a8a511ae2f4f796fc322c92a58796c"}},{"ruleId":"D2","level":"warning","message":{"text":"Operation::apply (cognitive 31): Operation::apply has cognitive complexity 31 (threshold 15). Drivers by points: if/else 11 (22 pts), boolean chains 6, loops 1 (2 pts), match/switch 1 (nesting depth added 12). 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":"crates/koharu-scene/src/patch.rs"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"016d59855e0dee190811b1a8afc429844335e8d9cebc0213ab539fbd9dedd186"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_agent::codex::stream::read (cognitive 30): koharu_agent::codex::stream::read has cognitive complexity 30 (threshold 15). Drivers by points: if/else 11 (23 pts), match/switch 2 (4 pts), boolean chains 2, loops 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":"crates/koharu-agent/src/codex/stream.rs"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"707be32c525e28988a1a16e633f35f4942318effaa0901f38a85f79ad7a8f787"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_app::commands::project::rasterize_stroke (cognitive 30): koharu_app::commands::project::rasterize_stroke has cognitive complexity 30 (threshold 15). Drivers by points: if/else 4 (14 pts), loops 4 (12 pts), match/switch 1 (4 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":"crates/koharu-app/src/commands/project.rs"},"region":{"startLine":1266}}}],"partialFingerprints":{"codehealthFindingId/v1":"1920c29858efb211d9cada50d81733422c127041cd5d2db19db6ef776f0815aa"}},{"ruleId":"D2","level":"warning","message":{"text":"AgentPanel.AgentPanel (cognitive 29): AgentPanel.AgentPanel has cognitive complexity 29 (threshold 15). Drivers by points: ternaries 11 (16 pts), boolean chains 7, if/else 5, error handling 1 (nesting depth added 5). Of this number, 17 points are the body\u0027s own statements and 12 belong to 7 function literals inside it that branch. 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":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"759cbdf3006d8d590f15d8795eae60ebfc01e071ddfddf51969d4adb77cf8a29"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::bin::comic_layout_yolo26s::annotated_image (cognitive 28): koharu_ml::bin::comic_layout_yolo26s::annotated_image has cognitive complexity 28 (threshold 15). Drivers by points: if/else 5 (13 pts), loops 5 (13 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":"crates/koharu-ml/src/bin/comic_layout_yolo26s.rs"},"region":{"startLine":172}}}],"partialFingerprints":{"codehealthFindingId/v1":"d010f2b8a601713c12eff97e4fb2975df05b6b45a801b104bfd458665f9bf085"}},{"ruleId":"D2","level":"warning","message":{"text":"Header::parse (cognitive 28): Header::parse has cognitive complexity 28 (threshold 15). Drivers by points: match/switch 7 (12 pts), if/else 5 (9 pts), boolean chains 5, loops 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":"crates/koharu-torch/src/tensor/npy.rs"},"region":{"startLine":80}}}],"partialFingerprints":{"codehealthFindingId/v1":"8438a9c12fbd4e39bb98397b05bc9268f8b6d911eb16803fe0060f48d6ac0d2f"}},{"ruleId":"D2","level":"warning","message":{"text":"ImageSlicer::merge_overlapping_boxes (cognitive 26): ImageSlicer::merge_overlapping_boxes has cognitive complexity 26 (threshold 15). Drivers by points: if/else 7 (22 pts), loops 2 (3 pts), boolean chains 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":"crates/koharu-ml/src/comic_text_bubble_detector/processor.rs"},"region":{"startLine":310}}}],"partialFingerprints":{"codehealthFindingId/v1":"5ef8d1087ee22a87707a48be7358b876550980f7edc06b3e8c9ed31436c817e2"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_psd::document::build (cognitive 26): koharu_psd::document::build has cognitive complexity 26 (threshold 15). Drivers by points: match/switch 5 (11 pts), if/else 7 (9 pts), boolean chains 4, loops 2 (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":"crates/koharu-psd/src/document.rs"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"75be0339ebd1064c76ddd2e7cf3e7c2ecb5b653bba40c80491a88c73b489b29f"}},{"ruleId":"D2","level":"warning","message":{"text":"Traversal::visit_children (cognitive 26): Traversal::visit_children has cognitive complexity 26 (threshold 15). Drivers by points: if/else 8 (17 pts), loops 2 (4 pts), match/switch 1 (4 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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":584}}}],"partialFingerprints":{"codehealthFindingId/v1":"2933808b030d8c0a33d643e0f19bb78f1d29036a7c61c8d332bcd5b90789f651"}},{"ruleId":"D2","level":"warning","message":{"text":"GpuCompositor::render (cognitive 25): GpuCompositor::render has cognitive complexity 25 (threshold 15). Drivers by points: if/else 8 (11 pts), boolean chains 6, loops 3 (5 pts), match/switch 2 (3 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":"crates/koharu-rasterizer/src/compositor.rs"},"region":{"startLine":389}}}],"partialFingerprints":{"codehealthFindingId/v1":"f1d2ac802463510d5162a95ae7d65bac8dc0e0b12cb2edcd28d5d6acc1de3218"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_runtime::hardware::hip::linux::probe (cognitive 25): koharu_runtime::hardware::hip::linux::probe has cognitive complexity 25 (threshold 15). Drivers by points: if/else 6 (13 pts), match/switch 2 (5 pts), loops 3 (4 pts), boolean chains 3 (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":"crates/koharu-runtime/src/hardware/hip/linux.rs"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"7bfa6283ff4863aaaf550976955c22d49c77cb2862efce602c06ce42c06cfe30"}},{"ruleId":"D2","level":"warning","message":{"text":"useCanvas.useCanvas (cognitive 25): useCanvas.useCanvas has cognitive complexity 25 (threshold 15). Drivers by points: if/else 13 (14 pts), boolean chains 5, ternaries 3 (4 pts), error handling 1 (2 pts) (nesting depth added 3). Most of this is not in the body itself: 0 of the 25 points are its own statements and the rest belongs to 11 function literals inside it that branch (lines 95, 130, 53, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/useCanvas.ts"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"f273d5f8a7ff23d173809d32a4dd3be72f8c536eb2cc67c2c620d54344a2a90f"}},{"ruleId":"D2","level":"warning","message":{"text":"SettingsPage.SettingsPage (cognitive 25): SettingsPage.SettingsPage has cognitive complexity 25 (threshold 15). Drivers by points: if/else 12 (14 pts), boolean chains 10, error handling 1 (nesting depth added 2). Most of this is not in the body itself: 8 of the 25 points are its own statements and the rest belongs to 6 function literals inside it that branch (lines 83, 144, 129, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/SettingsPage.tsx"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"9babc3b66a5ab2a99bf1bcdad08912164cd2d074fc8bcc2e14448a3b5728500d"}},{"ruleId":"D2","level":"warning","message":{"text":"Model::get_rope_index (cognitive 24): Model::get_rope_index has cognitive complexity 24 (threshold 15). Drivers by points: loops 6 (20 pts), if/else 3 (4 pts) (nesting depth added 15). 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":"crates/koharu-ml/src/paddle_ocr_vl/model.rs"},"region":{"startLine":237}}}],"partialFingerprints":{"codehealthFindingId/v1":"6ce448980525e9e49396c22340f31cd43cfd35eb47d5be36bdc6506254b37ee9"}},{"ruleId":"D2","level":"warning","message":{"text":"ModelPicker.ModelPicker (cognitive 24): ModelPicker.ModelPicker has cognitive complexity 24 (threshold 15). Drivers by points: boolean chains 14, ternaries 6, if/else 3 (4 pts) (nesting depth added 1). Most of this is not in the body itself: 7 of the 24 points are its own statements and the rest belongs to 4 function literals inside it that branch (lines 112, 137, 47, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"77f3792b2a4789fdbf52088283297fa71fdce97b3d7a30fec386d2bd146e0c05"}},{"ruleId":"D2","level":"warning","message":{"text":"TextLayout::append_balanced_segment_lines (cognitive 23): TextLayout::append_balanced_segment_lines has cognitive complexity 23 (threshold 15). Drivers by points: if/else 11 (15 pts), boolean chains 5, loops 2 (3 pts) (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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":955}}}],"partialFingerprints":{"codehealthFindingId/v1":"076b912e5821f7e94f1d6a7688aab79c60d2428ff7ae2b7ba47da00fb3e86050"}},{"ruleId":"D2","level":"warning","message":{"text":"Updater.Updater (cognitive 23): Updater.Updater has cognitive complexity 23 (threshold 15). Drivers by points: ternaries 7 (9 pts), boolean chains 8, if/else 6 (nesting depth added 2). Of this number, 15 points are the body\u0027s own statements and 8 belong to 5 function literals inside it that branch. 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":"packages/koharu/components/app/Updater.tsx"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"986233ab967e9b3a8b986792fcef903fbd144ef96033a1961d0613df54fb2c0f"}},{"ruleId":"D2","level":"warning","message":{"text":"Inspector.TypeInspector (cognitive 23): Inspector.TypeInspector has cognitive complexity 23 (threshold 15). Drivers by points: ternaries 13 (14 pts), if/else 5, boolean chains 4 (nesting depth added 1). Most of this is not in the body itself: 10 of the 23 points are its own statements and the rest belongs to 7 function literals inside it that branch (lines 385, 149, 414, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":131}}}],"partialFingerprints":{"codehealthFindingId/v1":"22b273555e14f266bca5f6df8b4444c87a2e7906b4bc0bbbd3b45317c5ecc520"}},{"ruleId":"D2","level":"warning","message":{"text":"geometry.layerFrame (cognitive 23): geometry.layerFrame has cognitive complexity 23 (threshold 15). Drivers by points: if/else 6 (10 pts), boolean chains 9, ternaries 3 (4 pts) (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":"packages/koharu/lib/geometry.ts"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd16a5ebd79d8035c4f1a0db61d019e861124c19607ad9eca67092ed5d77ee6e"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::comic_text_detector::processor::try_merge_text_line (cognitive 22): koharu_ml::comic_text_detector::processor::try_merge_text_line has cognitive complexity 22 (threshold 15). Drivers by points: if/else 10 (18 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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":752}}}],"partialFingerprints":{"codehealthFindingId/v1":"e0a5335a907be85f1c01af13d4a89f2175dd23b08faab2f8f5467a7eacea369a"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::pp_ocr_v6::det::processor::opencv_fill_polygon (cognitive 22): koharu_ml::pp_ocr_v6::det::processor::opencv_fill_polygon has cognitive complexity 22 (threshold 15). Drivers by points: if/else 6 (11 pts), loops 5 (10 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":"crates/koharu-ml/src/pp_ocr_v6/det/processor.rs"},"region":{"startLine":449}}}],"partialFingerprints":{"codehealthFindingId/v1":"dc6eb40efa43f2954ca41671b7dd0affced8bd50f532517850bb45641398a211"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::comic_onomatopoeia::detector::processor::fill_polygon (cognitive 22): koharu_ml::comic_onomatopoeia::detector::processor::fill_polygon has cognitive complexity 22 (threshold 15). Drivers by points: if/else 6 (11 pts), loops 5 (10 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":500}}}],"partialFingerprints":{"codehealthFindingId/v1":"b01b780e3e07f79712d0a12578f449308e2bdab8c3dde532cf704efef640f34d"}},{"ruleId":"D2","level":"warning","message":{"text":"Processor::process (cognitive 22): Processor::process has cognitive complexity 22 (threshold 15). Drivers by points: if/else 9 (17 pts), loops 3 (4 pts), boolean chains 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":"crates/koharu-pipeline/src/stages/translation.rs"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1898abd428e375b1eb4785e2dfd2d33a817ad1bf6a5193748e699927325d197"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::detection::enclosing_cluster_pixels (cognitive 22): koharu_pipeline::stages::detection::enclosing_cluster_pixels has cognitive complexity 22 (threshold 15). Drivers by points: loops 6 (12 pts), if/else 2 (8 pts), boolean chains 2 (nesting depth added 12). 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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":966}}}],"partialFingerprints":{"codehealthFindingId/v1":"ce9f7fbc8f61dc3d83618504033834a4df021b836b6e4c6a9a3b767881059839"}},{"ruleId":"D2","level":"warning","message":{"text":"TextRenderer::render_descriptor (cognitive 22): TextRenderer::render_descriptor has cognitive complexity 22 (threshold 15). Drivers by points: if/else 18, boolean chains 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":"crates/koharu-renderer/src/text_renderer.rs"},"region":{"startLine":148}}}],"partialFingerprints":{"codehealthFindingId/v1":"cca4c6f2b303283b8c308d66a26896d2f141d5821b42bcd20a4305babf9e45e5"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::shape::shape_script_runs (cognitive 22): koharu_renderer::shape::shape_script_runs has cognitive complexity 22 (threshold 15). Drivers by points: if/else 7 (14 pts), loops 3 (5 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":"crates/koharu-renderer/src/shape.rs"},"region":{"startLine":134}}}],"partialFingerprints":{"codehealthFindingId/v1":"885bc51d508d85093f4ebc0ed640045f7274e116ddb68978517e72bceef4e6f7"}},{"ruleId":"D2","level":"warning","message":{"text":"SelectionControls.SelectionControls (cognitive 22): SelectionControls.SelectionControls has cognitive complexity 22 (threshold 15). Drivers by points: boolean chains 10, if/else 8, ternaries 4. Most of this is not in the body itself: 0 of the 22 points are its own statements and the rest belongs to 7 function literals inside it that branch (lines 144, 86, 77, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/SelectionControls.tsx"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"d4f3eb842a9e521782b141345b1fb2d309cb2a27a11a4ea906c618e2e3856291"}},{"ruleId":"D2","level":"warning","message":{"text":"chart.ChartTooltipContent (cognitive 22): chart.ChartTooltipContent has cognitive complexity 22 (threshold 15). Drivers by points: boolean chains 9, ternaries 6 (9 pts), if/else 4 (nesting depth added 3). Most of this is not in the body itself: 4 of the 22 points are its own statements and the rest belongs to 2 function literals inside it that branch (lines 202, 149). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/chart.tsx"},"region":{"startLine":119}}}],"partialFingerprints":{"codehealthFindingId/v1":"43f3a91111c592f01b5531a53e81f345712c29492d9f2ee20b0419a8a5d2d0ea"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_app::commands::canvas::encode_mask (cognitive 21): koharu_app::commands::canvas::encode_mask has cognitive complexity 21 (threshold 15). Drivers by points: if/else 4 (10 pts), loops 3 (6 pts), match/switch 1 (4 pts), boolean chains 1 (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":"crates/koharu-app/src/commands/canvas.rs"},"region":{"startLine":414}}}],"partialFingerprints":{"codehealthFindingId/v1":"edfafd2081985a22be456c67b773f21bd8982d102ccb6cc2860bf36a120b335c"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::detection::measured_stroke_width (cognitive 21): koharu_pipeline::stages::detection::measured_stroke_width has cognitive complexity 21 (threshold 15). Drivers by points: if/else 6 (13 pts), loops 4 (7 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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":1180}}}],"partialFingerprints":{"codehealthFindingId/v1":"7587ddd35182d92cb4cb4e46d3fc1e07805274930e3059c9e9c22dac7aee2d2c"}},{"ruleId":"D2","level":"warning","message":{"text":"Edit::remove_entity (cognitive 21): Edit::remove_entity has cognitive complexity 21 (threshold 15). Drivers by points: loops 5 (9 pts), if/else 7, boolean chains 4, 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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":202}}}],"partialFingerprints":{"codehealthFindingId/v1":"a8f062011e44bac452acb140587b1f74bfabad49e856c3be7a9574ca56dcaf24"}},{"ruleId":"D2","level":"warning","message":{"text":"Project::apply_raster_stroke (cognitive 20): Project::apply_raster_stroke has cognitive complexity 20 (threshold 15). Drivers by points: if/else 11 (14 pts), boolean chains 4, match/switch 1 (2 pts) (nesting depth added 4). 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":"crates/koharu-app/src/commands/project.rs"},"region":{"startLine":741}}}],"partialFingerprints":{"codehealthFindingId/v1":"291896912bfca52275086233c399fcd4a217740f056abe3eea1e7bd55d6ae21a"}},{"ruleId":"D2","level":"warning","message":{"text":"Model::run (cognitive 20): Model::run has cognitive complexity 20 (threshold 15). Drivers by points: if/else 12 (16 pts), boolean chains 2, loops 1, 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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":177}}}],"partialFingerprints":{"codehealthFindingId/v1":"aea4727112252ae6874363c9a528b9ff9c69347a319b0f77ed9c4bb2308166a8"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::detection::stamp_mask (cognitive 20): koharu_pipeline::stages::detection::stamp_mask has cognitive complexity 20 (threshold 15). Drivers by points: if/else 7 (16 pts), loops 2 (3 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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":1539}}}],"partialFingerprints":{"codehealthFindingId/v1":"d91a53414776971de7c580354ff43f0dc51813effb135633dd0737d28d190eb1"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_psd::engine_data::write_value (cognitive 20): koharu_psd::engine_data::write_value has cognitive complexity 20 (threshold 15). Drivers by points: loops 4 (10 pts), if/else 6 (9 pts), match/switch 1 (nesting depth added 9). 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":"crates/koharu-psd/src/engine_data.rs"},"region":{"startLine":616}}}],"partialFingerprints":{"codehealthFindingId/v1":"224534ebeec37de37edb49827436ff6f7fa922f73fa966a019cf02e821cb6b00"}},{"ruleId":"D2","level":"warning","message":{"text":"TextLayout::ink_bounds (cognitive 20): TextLayout::ink_bounds has cognitive complexity 20 (threshold 15). Drivers by points: if/else 6 (14 pts), loops 2 (3 pts), match/switch 1 (3 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1139}}}],"partialFingerprints":{"codehealthFindingId/v1":"ee17b5bf90dfa118e2fa99a6894fe01bfbee28f5c8673add2f7788c498243239"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::text_renderer::draw_layout (cognitive 20): koharu_renderer::text_renderer::draw_layout has cognitive complexity 20 (threshold 15). Drivers by points: loops 4 (9 pts), if/else 3 (7 pts), boolean chains 2, match/switch 1 (2 pts) (nesting depth added 10). 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":"crates/koharu-renderer/src/text_renderer.rs"},"region":{"startLine":367}}}],"partialFingerprints":{"codehealthFindingId/v1":"bc482ca920b7808b70fbd0e25a0c096472c22481e9caf7d441a0211de9581d5a"}},{"ruleId":"D2","level":"warning","message":{"text":"Tensor::f_indexer (cognitive 20): Tensor::f_indexer has cognitive complexity 20 (threshold 15). Drivers by points: if/else 5 (10 pts), match/switch 3 (8 pts), loops 2 (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":"crates/koharu-torch/src/tensor/index.rs"},"region":{"startLine":293}}}],"partialFingerprints":{"codehealthFindingId/v1":"51493bf9c951709dce1aa9f9fc99e8382198cffc2ca11ede8150fd1639aafa32"}},{"ruleId":"D2","level":"warning","message":{"text":"InferenceControl.RuntimeSelector (cognitive 20): InferenceControl.RuntimeSelector has cognitive complexity 20 (threshold 15). Drivers by points: boolean chains 12, if/else 6 (7 pts), ternaries 1 (nesting depth added 1). Most of this is not in the body itself: 8 of the 20 points are its own statements and the rest belongs to 5 function literals inside it that branch (lines 133, 162, 181, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/InferenceControl.tsx"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"b95f6bd32623c38cb08e6023cacdb115649a71d20dd66f602e1e5bd4de8d0d29"}},{"ruleId":"D2","level":"warning","message":{"text":"CanvasState::composition (cognitive 19): CanvasState::composition has cognitive complexity 19 (threshold 15). Drivers by points: if/else 8 (14 pts), boolean chains 2, match/switch 1 (2 pts), loops 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":"crates/koharu-canvas/src/web.rs"},"region":{"startLine":788}}}],"partialFingerprints":{"codehealthFindingId/v1":"d038302c9b3f0a5088f9373803a6e4fba6d614fe8ab8d24b65d768d6c5cf1900"}},{"ruleId":"D2","level":"warning","message":{"text":"Flux2ImageProcessor::get_crop_region (cognitive 19): Flux2ImageProcessor::get_crop_region has cognitive complexity 19 (threshold 15). Drivers by points: if/else 10 (17 pts), boolean chains 1, loops 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":"crates/koharu-ml/src/flux2_klein/processor.rs"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"ffde9c6bc74b25b0be333978f2ecd3fb4a4e6c93b167fccff613e9906d47d41c"}},{"ruleId":"D2","level":"warning","message":{"text":"Model::beam_search (cognitive 19): Model::beam_search has cognitive complexity 19 (threshold 15). Drivers by points: if/else 6 (14 pts), loops 3 (5 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":"crates/koharu-ml/src/manga_ocr/model.rs"},"region":{"startLine":81}}}],"partialFingerprints":{"codehealthFindingId/v1":"02813a00b7a11346a55f88df3479a92752126e44ffdc63b05eb2682d590b74a7"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::detection::closed_mask_for (cognitive 19): koharu_pipeline::stages::detection::closed_mask_for has cognitive complexity 19 (threshold 15). Drivers by points: loops 7 (9 pts), if/else 4 (8 pts), boolean chains 2 (nesting depth added 6). 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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":1461}}}],"partialFingerprints":{"codehealthFindingId/v1":"e2d92877e5cdc2bc18540f4e29b8f7f40667113a9ef3b8d6c117d420d76f93f0"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_rasterizer::frame::compile_frame (cognitive 19): koharu_rasterizer::frame::compile_frame has cognitive complexity 19 (threshold 15). Drivers by points: if/else 4 (13 pts), loops 2 (4 pts), match/switch 1 (2 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":"crates/koharu-rasterizer/src/frame.rs"},"region":{"startLine":345}}}],"partialFingerprints":{"codehealthFindingId/v1":"6a25463e63c4a4949a31e03d4a940a3c8506eeca4876cdad294e1696d3b6d905"}},{"ruleId":"D2","level":"warning","message":{"text":"FontSystem::resolve_with_fallback_families (cognitive 19): FontSystem::resolve_with_fallback_families has cognitive complexity 19 (threshold 15). Drivers by points: if/else 10 (14 pts), loops 2 (3 pts), boolean chains 1, match/switch 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":"crates/koharu-renderer/src/fonts.rs"},"region":{"startLine":414}}}],"partialFingerprints":{"codehealthFindingId/v1":"3da537359af1a3eecd342c07754f38e7756ef8835b50eda20dfddab075293a42"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::layout::largest_fitting_font_size (cognitive 19): koharu_renderer::layout::largest_fitting_font_size has cognitive complexity 19 (threshold 15). Drivers by points: if/else 7 (14 pts), loops 2 (4 pts), boolean chains 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":194}}}],"partialFingerprints":{"codehealthFindingId/v1":"44e589b4d3977ffc9cf30e50ef08ded702a63cb852eeaafeb15fb84fb43d0946"}},{"ruleId":"D2","level":"warning","message":{"text":"BlobStore::collect (cognitive 19): BlobStore::collect has cognitive complexity 19 (threshold 15). Drivers by points: if/else 7 (16 pts), loops 2 (3 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":"crates/koharu-storage/src/blobs.rs"},"region":{"startLine":91}}}],"partialFingerprints":{"codehealthFindingId/v1":"c629b65f0c3ed4346a501c65305204463f29eac93e16e530e9a748eb25986b4a"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_torch::nn::init::f_init (cognitive 19): koharu_torch::nn::init::f_init has cognitive complexity 19 (threshold 15). Drivers by points: if/else 10 (15 pts), match/switch 2 (3 pts), boolean chains 1 (nesting depth added 6). 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":"crates/koharu-torch/src/nn/init.rs"},"region":{"startLine":108}}}],"partialFingerprints":{"codehealthFindingId/v1":"c2b1a32d75514f5c0b3809e44e8d3fd7469f79a32a041a559d373b090fe0871b"}},{"ruleId":"D2","level":"warning","message":{"text":"CanvasOverlay.CanvasOverlay (cognitive 19): CanvasOverlay.CanvasOverlay has cognitive complexity 19 (threshold 15). Drivers by points: boolean chains 11, ternaries 7, if/else 1. Most of this is not in the body itself: 9 of the 19 points are its own statements and the rest belongs to 3 function literals inside it that branch (lines 88, 57, 75). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasOverlay.tsx"},"region":{"startLine":33}}}],"partialFingerprints":{"codehealthFindingId/v1":"ae968cd11815e3f3b0673d98f356f0ac50d6dbcc9cb89ff2958a3977ab0d7310"}},{"ruleId":"D2","level":"warning","message":{"text":"CanvasState::activate_manifest (cognitive 18): CanvasState::activate_manifest has cognitive complexity 18 (threshold 15). Drivers by points: if/else 9 (12 pts), boolean chains 5, 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":"crates/koharu-canvas/src/web.rs"},"region":{"startLine":657}}}],"partialFingerprints":{"codehealthFindingId/v1":"d4609eb9deb8200dfa8fd073c0d2d66376f13433855809d9a18be371cc63b8d1"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::comic_text_detector::processor::sort_regions (cognitive 18): koharu_ml::comic_text_detector::processor::sort_regions has cognitive complexity 18 (threshold 15). Drivers by points: if/else 5 (12 pts), boolean chains 3, loops 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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":818}}}],"partialFingerprints":{"codehealthFindingId/v1":"0eb531c9d16cb6071314cf06816807b70e51ca91ba4d8ae015d1c8e6e9de09fb"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::speech_bubble_yolov8m::processor::non_max_suppression (cognitive 18): koharu_ml::speech_bubble_yolov8m::processor::non_max_suppression has cognitive complexity 18 (threshold 15). Drivers by points: if/else 5 (10 pts), loops 4 (6 pts), boolean chains 2 (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":"crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs"},"region":{"startLine":308}}}],"partialFingerprints":{"codehealthFindingId/v1":"299e76359d9d828c703fc8b6b1bdabca360a5eb8f848fc71fc2eb36f6ac633e2"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::manga_text_mask::processor::fill_holes (cognitive 18): koharu_ml::manga_text_mask::processor::fill_holes has cognitive complexity 18 (threshold 15). Drivers by points: loops 6 (9 pts), if/else 4 (7 pts), boolean chains 2 (nesting depth added 6). 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":"crates/koharu-ml/src/manga_text_mask/processor.rs"},"region":{"startLine":292}}}],"partialFingerprints":{"codehealthFindingId/v1":"6dbe3544b306c24de0dc43d2b152a00d1e79f498f40c06a8afe0eeea24509d67"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::inpainting::inpaint_tiles (cognitive 18): koharu_pipeline::stages::inpainting::inpaint_tiles has cognitive complexity 18 (threshold 15). Drivers by points: if/else 4 (10 pts), loops 3 (6 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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":619}}}],"partialFingerprints":{"codehealthFindingId/v1":"0aeeaf7e680e5a2a3eda882fd09c3a10d9b272ae7b9121860211945828b54dce"}},{"ruleId":"D2","level":"warning","message":{"text":"TextLayout::run_auto (cognitive 18): TextLayout::run_auto has cognitive complexity 18 (threshold 15). Drivers by points: if/else 8 (12 pts), match/switch 1 (3 pts), boolean chains 2, loops 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":372}}}],"partialFingerprints":{"codehealthFindingId/v1":"4538b14ac1f1ea220836235331d6b193f0379025048e812d542bb6ef2e6f3aa3"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::layout::justify_lines (cognitive 18): koharu_renderer::layout::justify_lines has cognitive complexity 18 (threshold 15). Drivers by points: if/else 5 (10 pts), boolean chains 5, loops 2 (3 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1362}}}],"partialFingerprints":{"codehealthFindingId/v1":"61a117ae9b5deac761b612857eec8eca59bd6f2d483c71ae01e19cab6777abf8"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_renderer::bubble::geometry_frame (cognitive 18): koharu_renderer::bubble::geometry_frame has cognitive complexity 18 (threshold 15). Drivers by points: if/else 6 (9 pts), boolean chains 7, loops 1 (2 pts) (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":"crates/koharu-renderer/src/bubble.rs"},"region":{"startLine":643}}}],"partialFingerprints":{"codehealthFindingId/v1":"723df294524be94e5543a4e602a5cdd2176e9b4ac72c56064a006873bf875c42"}},{"ruleId":"D2","level":"warning","message":{"text":"canvas.prefetchCanvasPages (cognitive 18): canvas.prefetchCanvasPages has cognitive complexity 18 (threshold 15). Drivers by points: if/else 6 (12 pts), boolean chains 3, loops 2 (3 pts) (nesting depth added 7). Of this number, 17 points are the body\u0027s own statements and 1 belongs to one function literal inside it that branches. 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":"packages/bridge/src/canvas.ts"},"region":{"startLine":246}}}],"partialFingerprints":{"codehealthFindingId/v1":"d25723f745a7ae33fe961feb5e6b9d0a436b9e40a1ef9eab040fbe4534d1334e"}},{"ruleId":"D2","level":"warning","message":{"text":"GridGenerator::new (cognitive 17): GridGenerator::new has cognitive complexity 17 (threshold 15). Drivers by points: loops 8 (13 pts), if/else 2 (4 pts) (nesting depth added 7). 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":"crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs"},"region":{"startLine":237}}}],"partialFingerprints":{"codehealthFindingId/v1":"f14596910b378221184e548dac40989d6faa823a81e0b19b7aa340058997d517"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::pp_ocr_v6::det::processor::draw_line_8_connected (cognitive 17): koharu_ml::pp_ocr_v6::det::processor::draw_line_8_connected has cognitive complexity 17 (threshold 15). Drivers by points: if/else 11 (15 pts), boolean chains 1, loops 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":"crates/koharu-ml/src/pp_ocr_v6/det/processor.rs"},"region":{"startLine":507}}}],"partialFingerprints":{"codehealthFindingId/v1":"2bee910fa842f2b578dc42b04a64634b0a4ab1c0ae4fd28b0c9f3719d6a5b8c3"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::comic_onomatopoeia::detector::processor::draw_line_8_connected (cognitive 17): koharu_ml::comic_onomatopoeia::detector::processor::draw_line_8_connected has cognitive complexity 17 (threshold 15). Drivers by points: if/else 11 (15 pts), boolean chains 1, loops 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":557}}}],"partialFingerprints":{"codehealthFindingId/v1":"e9ba06bd74ed85f305a54b64bdf09eb4fcac955cdd7a0df6f523501ae2cd70c4"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_ml::speech_bubble_yolo11n::processor::non_max_suppression (cognitive 17): koharu_ml::speech_bubble_yolo11n::processor::non_max_suppression has cognitive complexity 17 (threshold 15). Drivers by points: if/else 5 (11 pts), loops 3 (4 pts), boolean chains 2 (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":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":304}}}],"partialFingerprints":{"codehealthFindingId/v1":"8f5f7572f7305b669981d9dbe662608ecb02bec87a7377f859f1ea091ba1bb46"}},{"ruleId":"D2","level":"warning","message":{"text":"Model::run (cognitive 17): Model::run has cognitive complexity 17 (threshold 15). Drivers by points: if/else 5 (10 pts), loops 4 (5 pts), match/switch 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":"crates/koharu-pipeline/src/stages/ocr.rs"},"region":{"startLine":92}}}],"partialFingerprints":{"codehealthFindingId/v1":"6f942adbba41f35777db769b046aa454f6fd1a2589c4b84d3602d90b8dbb35c3"}},{"ruleId":"D2","level":"warning","message":{"text":"koharu_pipeline::stages::inpainting::fill_uniform_regions (cognitive 17): koharu_pipeline::stages::inpainting::fill_uniform_regions has cognitive complexity 17 (threshold 15). Drivers by points: if/else 3 (8 pts), loops 4 (8 pts), boolean chains 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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":587}}}],"partialFingerprints":{"codehealthFindingId/v1":"7fe2fcc236003aea42a9813e79d694f161282a5154f8aa0b34b2fff5eebf05e5"}},{"ruleId":"D2","level":"warning","message":{"text":"Fonts::resolve (cognitive 17): Fonts::resolve has cognitive complexity 17 (threshold 15). Drivers by points: if/else 10 (12 pts), boolean chains 2, match/switch 1 (2 pts), 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":"crates/koharu-renderer/src/fonts.rs"},"region":{"startLine":697}}}],"partialFingerprints":{"codehealthFindingId/v1":"7e0830a1f21eb6d7fb70b5fc4cac935c88707d27fa722c1318f919680ec42c76"}},{"ruleId":"D2","level":"warning","message":{"text":"VarStore::merge (cognitive 17): VarStore::merge has cognitive complexity 17 (threshold 15). Drivers by points: loops 3 (8 pts), if/else 3 (5 pts), match/switch 1 (4 pts) (nesting depth added 10). 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":"crates/koharu-torch/src/nn/var_store.rs"},"region":{"startLine":68}}}],"partialFingerprints":{"codehealthFindingId/v1":"387d43ab69c6c7fdff21be7cd92edfcb902b57669832a605978077443f682277"}},{"ruleId":"D2","level":"warning","message":{"text":"providers.Providers (cognitive 17): providers.Providers has cognitive complexity 17 (threshold 15). Drivers by points: if/else 8 (13 pts), ternaries 1 (3 pts), boolean chains 1 (nesting depth added 7). Most of this is not in the body itself: 0 of the 17 points are its own statements and the rest belongs to 6 function literals inside it that branch (lines 62, 52, 36, \u2026). The decisions are inside those literals, which nothing outside this body can call, review or test on its own, so splitting the enclosing body is not the move available here. To reduce it, lift the literals\u0027 work into a named function or method at the enclosing scope and have each literal call it, then reduce whichever part then reads as the largest."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/providers.tsx"},"region":{"startLine":33}}}],"partialFingerprints":{"codehealthFindingId/v1":"29e5ad5ef58036eb0f4c5b791c26ce7d7793c0f7392b2b0200f27417d9f41707"}},{"ruleId":"D2","level":"warning","message":{"text":"FontPicker.fontNameScript (cognitive 17): FontPicker.fontNameScript has cognitive complexity 17 (threshold 15). Drivers by points: if/else 8 (16 pts), loops 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":"packages/koharu/components/controls/FontPicker.tsx"},"region":{"startLine":525}}}],"partialFingerprints":{"codehealthFindingId/v1":"8df9859836ef7ed8d557e2727e6b85956d7ab5c8cf4f7888e38a2ce0977a631b"}},{"ruleId":"D2","level":"warning","message":{"text":"Traversal::text_draft (cognitive 16): Traversal::text_draft has cognitive complexity 16 (threshold 15). Drivers by points: if/else 12 (14 pts), boolean chains 1, loops 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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":714}}}],"partialFingerprints":{"codehealthFindingId/v1":"42e963037b1b853fd1bf279d210dcffcc6d9cfee6e49cc3b5d7de566ed981b2f"}},{"ruleId":"D2","level":"warning","message":{"text":"LineBreaker::hyphenated_segments (cognitive 16): LineBreaker::hyphenated_segments has cognitive complexity 16 (threshold 15). Drivers by points: if/else 11 (14 pts), boolean chains 1, 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":"crates/koharu-renderer/src/segment.rs"},"region":{"startLine":184}}}],"partialFingerprints":{"codehealthFindingId/v1":"33f30525f2624a26184e8364bb2f0e6e1b95783b27598a31cef27181eb3013d9"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Tensor: TooManyMethods \u2014 5422 methods, declared across 11 files: wrappers/tensor_fallible_generated.rs (2657), wrappers/tensor_generated.rs (2657), wrappers/tensor.rs (71), tensor/mod.rs (22), \u002B7 more file(s). The bar is 30 methods; this is 5392 over it, 180.73\u00D7 the bar. That list is where to read them, not a suggestion to split the file: the members belong to the type wherever they are declared, so moving them between files leaves the count unchanged. 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":"crates/koharu-torch/src/wrappers/tensor.rs"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"306e570a7c82ea9f615113e864cd16637bd105e0ffe6ceab03106232380175f1"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: CanvasWorkspace.CanvasWorkspace: FunctionTooLong \u2014 CanvasWorkspace runs 606 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 506 over it, 6.06\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":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"e3b220516633313878521e26280c85ea59faba2606d24898c01f963901c8c0f0"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: pp_doclayout_v3/model.rs: FileTooLong \u2014 1778 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 1278 over it, 3.56\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/pp_doclayout_v3/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b23f58bb024221abfc4043f663666fac3faa672a843bf7c5907feebea9c24cf"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: TextLayout.run_with_size: MethodTooLong \u2014 run_with_size runs 319 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 219 over it, 3.19\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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":463}}}],"partialFingerprints":{"codehealthFindingId/v1":"f519385f8ad0f63b606b35603804545617f1fc938cd8ea37c40304aa873eac65"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/layout.rs: FileTooLong \u2014 1536 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 1036 over it, 3.07\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"fc697e691e3b6960aeb2b43ce2769e8f0a8d5aa8f7e714bb29a6e5029a50eadd"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/web.rs: FileTooLong \u2014 1534 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 1034 over it, 3.07\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-canvas/src/web.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"404e39986ba0d48e393df5adf3d844bb8b52192cf05110cd1b11867af405e7dc"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: stages/detection.rs: FileTooLong \u2014 1368 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), declaring 66 free functions. The bar is 500 significant lines; this is 868 over it, 2.74\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"0157b4a9bdf9f98245bd9ec4f10cc5b679c5f9ba1198d5632f21f732031723ff"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: AgentPanel.AgentPanel: FunctionTooLong \u2014 AgentPanel runs 272 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 172 over it, 2.72\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":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"8901012e11f4f1d151841f25dbae3fc0e4250adc2bf3b7370aa691828f257647"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: PageRail.PageRail: FunctionTooLong \u2014 PageRail runs 269 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 169 over it, 2.69\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":"packages/koharu/components/editor/PageRail.tsx"},"region":{"startLine":69}}}],"partialFingerprints":{"codehealthFindingId/v1":"0901da6f5e7a36e0388e010e75f25afd2ca235b1aeea33bbd0691f85a3b5400a"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: comic_text_bubble_detector/model.rs: FileTooLong \u2014 1324 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 824 over it, 2.65\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"37e8bd3862ff10a518d4950c718fc5e13d1268639066d6d1e3e2166005f05f56"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: Inspector.TypeInspector: FunctionTooLong \u2014 TypeInspector runs 262 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 162 over it, 2.62\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":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":131}}}],"partialFingerprints":{"codehealthFindingId/v1":"5fa10a599940155ff17576cf0c61e41abdc3eee9429da9dc033cbeeebbbfd4d7"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/renderer.rs: FileTooLong \u2014 1301 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 801 over it, 2.60\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f467bfc961dd011d4863c5e2cfc8178718abea9d17f8f36118042cc8e54d6a1c"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: StartView.StartView: FunctionTooLong \u2014 StartView runs 225 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 125 over it, 2.25\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":"packages/koharu/components/start/StartView.tsx"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"e701507b96bc104d4ad270f8f974e5061ec4b82b5168d7da1f2b7e313a5e5973"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: InferenceControl.RuntimeSelector: FunctionTooLong \u2014 RuntimeSelector runs 224 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 124 over it, 2.24\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":"packages/koharu/components/editor/InferenceControl.tsx"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"3ba302a868990aed3138435391b3262002d3446518a9d3d213f0192f1fe68492"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: TitleBar.TitleBar: FunctionTooLong \u2014 TitleBar runs 223 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 123 over it, 2.23\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":"packages/koharu/components/app/TitleBar.tsx"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"1cd6d495d14483ddc6421f345e2159d9507c33204218107b5a45c50b6c34b4a9"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: TextLayout: ClassTooLong \u2014 840 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 25 methods, 2 blocks, lines 174-1352. The bar is 400 significant lines; this is 440 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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":174}}}],"partialFingerprints":{"codehealthFindingId/v1":"4b6a37665c12b582b485751233d9e6436b30051b17d687cedade72da9ec135e7"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: commands/project.rs: FileTooLong \u2014 1034 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), about 75% of them inside a single declaration: Project (2 blocks, 254-1238). The bar is 500 significant lines; this is 534 over it, 2.07\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":"crates/koharu-app/src/commands/project.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"5078c0f577883a17fc7e3eda63ecb336578f8f160b9abf94e0830b309228d625"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: comic_text_detector/processor.rs: FileTooLong \u2014 1024 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), declaring 43 free functions. The bar is 500 significant lines; this is 524 over it, 2.05\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"d5dae27d9b4c92048d740e5011a34d6fbf8d173ed0a406e6c59a14a13ac560ac"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: Project: ClassTooLong \u2014 773 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 45 methods, 2 blocks, lines 254-1238. The bar is 400 significant lines; this is 373 over it, 1.93\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":"crates/koharu-app/src/commands/project.rs"},"region":{"startLine":254}}}],"partialFingerprints":{"codehealthFindingId/v1":"d75ff8965c57a967d92f4e040bd904f778573bb07a0a9c0c209e70d75f569b84"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: LlamaContextParams: 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":"crates/koharu-llama/src/context/params.rs"},"region":{"startLine":301}}}],"partialFingerprints":{"codehealthFindingId/v1":"83ac060fca270e6f02db02ac94140c128a4b01b3712c26683b07155b197b0092"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFields: RTDetrV2Config: TooManyFields \u2014 56 stored fields beside 1 method. The bar is 30 stored fields; this is 26 over it, 1.87\u00D7 the bar. This is width in DATA, not behaviour: every reader that takes the whole type couples to all of its fields, so a change to any one of them is a change every reader has to be checked against. To reduce it, group the fields that are read together by the same callers into a smaller type of their own, and have this one hold that type as a single member \u2014 each reader then names only the group it uses."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/config.rs"},"region":{"startLine":12}}}],"partialFingerprints":{"codehealthFindingId/v1":"eb123aaac3abd3620974f70c31830ba51e80ac270cb32eca02398776672aabf8"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/params.rs: FileTooLong \u2014 929 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 429 over it, 1.86\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-diffusion/src/params.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"dff0cd4190c85baf53527e03a22a47b4b03f8020299f5302e69750fe2d63de9c"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: local/catalog.rs: FileTooLong \u2014 924 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 424 over it, 1.85\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-translator/src/local/catalog.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"c56e22933eebb13953489dd3f8bfeb466c8afe277c9b47ceeaa2c2a6100700f3"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: editor/Inspector.tsx: FileTooLong \u2014 899 significant lines (blank, comment-only and punctuation-only lines excluded). The bar is 500 significant lines; this is 399 over it, 1.80\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"2f3fa27ee07e8434f01d4d9a791c0e36cf9a6512b799427069272ac7fc0590cd"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: koharu_layout_rfdetr_seg_2xl/model.rs: FileTooLong \u2014 892 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 392 over it, 1.78\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"42278eddb59cf0a7e93d99e625ad64f07bb1be55874326ff971654c05411f072"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/fonts.rs: FileTooLong \u2014 881 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 381 over it, 1.76\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-renderer/src/fonts.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"c28c080472df676bcf70f2bac6d07e215448e8fadeef3b5933d640731861b292"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: koharu_app::app::run: FunctionTooLong \u2014 koharu_app::app::run runs 173 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 73 over it, 1.73\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":"crates/koharu-app/src/app.rs"},"region":{"startLine":68}}}],"partialFingerprints":{"codehealthFindingId/v1":"de277a24278f49b8651a8262ad0e2a5a2832c81163c1e0f223190fe649d52c01"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: Edit: ClassTooLong \u2014 665 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 44 methods, 2 blocks, lines 31-905. The bar is 400 significant lines; this is 265 over it, 1.66\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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"a104244418e58802f633ebd9bc6f517aba5084247e438d0b88c11ea8f6aeda48"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: koharu_psd::engine_data::encode_engine_data: FunctionTooLong \u2014 koharu_psd::engine_data::encode_engine_data runs 166 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 66 over it, 1.66\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":"crates/koharu-psd/src/engine_data.rs"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"68b369d198bb58f417b9055eed3ff5c69f20cf24b9f24e188dab10d09474bbf6"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/state.rs: FileTooLong \u2014 816 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 316 over it, 1.63\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-scene/src/state.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"e54c8a8de10145622fce69e1fc9ee637feacbb9a3dca4054347a3cf649e3d329"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: KoharuHost.invoke: MethodTooLong \u2014 invoke runs 162 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 62 over it, 1.62\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":"crates/koharu-app/src/commands/agent/host.rs"},"region":{"startLine":239}}}],"partialFingerprints":{"codehealthFindingId/v1":"b307d027169dbdb43a67e1a05f5661debb1b6fb99fd73841099883552bb76a7b"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: koharu_app::commands::processing::process: FunctionTooLong \u2014 koharu_app::commands::processing::process runs 162 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 62 over it, 1.62\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":"crates/koharu-app/src/commands/processing.rs"},"region":{"startLine":77}}}],"partialFingerprints":{"codehealthFindingId/v1":"ebfd0cc7029d3b41d16ee3cd441d739edf18760c6417471c3c93e11dd90f7f35"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: koharu_ml::comic_text_detector::processor::rearranged_inference: FunctionTooLong \u2014 koharu_ml::comic_text_detector::processor::rearranged_inference runs 162 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 62 over it, 1.62\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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":114}}}],"partialFingerprints":{"codehealthFindingId/v1":"73b7a61d502979b1e85434ccd790133a280d11e41b86503640510746bc5ba301"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: paddle_ocr_vl/model.rs: FileTooLong \u2014 783 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 283 over it, 1.57\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/paddle_ocr_vl/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"82854d29c89757ea5b7fab4c5ef24aa9fa154a134c8b81606acdcfac41eb1f72"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: SettingsPage.SettingsPage: FunctionTooLong \u2014 SettingsPage runs 155 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 55 over it, 1.55\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":"packages/koharu/components/preferences/SettingsPage.tsx"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"4b16a2657214e437d162cd03ff01cb35f46aab7b101269f8f4bd4c51d75d287a"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: editor/CanvasWorkspace.tsx: FileTooLong \u2014 772 significant lines (blank, comment-only and punctuation-only lines excluded), about 78% of them inside a single declaration: CanvasWorkspace (83-812). The bar is 500 significant lines; this is 272 over it, 1.54\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":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"6af429df43f7ea465bfabbf97072d6423bd119a5fe8ca79bdc5141d7c6595dfc"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: LlamaContext: TooManyMethods \u2014 46 methods, declared across 3 files: src/context.rs (21), context/session.rs (14), context/kv_cache.rs (11). The bar is 30 methods; this is 16 over it, 1.53\u00D7 the bar. That list is where to read them, not a suggestion to split the file: the members belong to the type wherever they are declared, so moving them between files leaves the count unchanged. 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":"crates/koharu-llama/src/context.rs"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"71263ef34fc6464d24caf9396064497fb69afcf0ab3d943d31feedb345904f93"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: CanvasWorkspace: ClassTooLong \u2014 606 significant lines (blank, comment-only and punctuation-only lines excluded), 9 methods. The bar is 400 significant lines; this is 206 over it, 1.52\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":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"dcdf7418b8e9323bd5bf46f71509b43dc7e7eb324bec0da4fffd821aaf85b1d6"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Project: 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":"crates/koharu-app/src/commands/project.rs"},"region":{"startLine":254}}}],"partialFingerprints":{"codehealthFindingId/v1":"cf830f5beb16b19c4b42f5a694fecab1e5583dea726b46f3c8cc84aa6597a736"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFields: PPDocLayoutV3Config: TooManyFields \u2014 45 stored fields beside 3 methods. The bar is 30 stored fields; this is 15 over it, 1.50\u00D7 the bar. This is width in DATA, not behaviour: every reader that takes the whole type couples to all of its fields, so a change to any one of them is a change every reader has to be checked against. To reduce it, group the fields that are read together by the same callers into a smaller type of their own, and have this one hold that type as a single member \u2014 each reader then names only the group it uses."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/pp_doclayout_v3/config.rs"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"3cd5379012c3bffef9f30f053395156ca38aa8c47aaf38c1832e3f1e60e37270"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyFields: ContextParams: TooManyFields \u2014 45 stored fields beside 1 method. The bar is 30 stored fields; this is 15 over it, 1.50\u00D7 the bar. This is width in DATA, not behaviour: every reader that takes the whole type couples to all of its fields, so a change to any one of them is a change every reader has to be checked against. To reduce it, group the fields that are read together by the same callers into a smaller type of their own, and have this one hold that type as a single member \u2014 each reader then names only the group it uses."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-diffusion/src/params.rs"},"region":{"startLine":74}}}],"partialFingerprints":{"codehealthFindingId/v1":"d0141cb7390fd4871bc3c5e5db53867c39ed30f3df4b4a489434937b0fe6725b"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Model.run: MethodTooLong \u2014 run runs 150 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 50 over it, 1.50\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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":177}}}],"partialFingerprints":{"codehealthFindingId/v1":"2ec435caa9e1d40abbe39fa318862ed2f518a5cab4536fbfca6ac498b6c60f56"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: manga_ocr/model.rs: FileTooLong \u2014 741 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 241 over it, 1.48\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/manga_ocr/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"c52f486c498624b46c929b481210a25fd2beae33d7ae999fd60dd8e5b0c9400d"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: LlamaModel: TooManyMethods \u2014 44 methods. The bar is 30 methods; this is 14 over it, 1.47\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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"516493df72550e331174a39ded46fff5cfa204fd06e43aee6224ae816c59753e"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Edit: TooManyMethods \u2014 44 methods, declared across 2 files: src/edit.rs (41), src/document.rs (3). The bar is 30 methods; this is 14 over it, 1.47\u00D7 the bar. That list is where to read them, not a suggestion to split the file: the members belong to the type wherever they are declared, so moving them between files leaves the count unchanged. 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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"44a26a2685d5fe9bc55540e3e59fca7822f047eb205044db73360204c6c91557"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: TextRenderer.render_descriptor: MethodTooLong \u2014 render_descriptor runs 145 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 45 over it, 1.45\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":"crates/koharu-renderer/src/text_renderer.rs"},"region":{"startLine":148}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca85f52912fd1d558501a58c07cb5f3055968b9e58e6b92c2e89589041c7d138"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: baberu_ocr/model.rs: FileTooLong \u2014 718 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 218 over it, 1.44\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/baberu_ocr/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"e4f3863c63ae363beb9b127b5387814996369e4de2b961eb31a0c0a4ecb0f9bb"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: comic_text_detector/model.rs: FileTooLong \u2014 710 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 210 over it, 1.42\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_detector/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"9ba762aa35cc36e70a8b0b55a2e04f87c84610ddbc872a80b99dedee08f9dc2a"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: YoloV5.new: MethodTooLong \u2014 new runs 142 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 42 over it, 1.42\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":"crates/koharu-ml/src/comic_text_detector/model.rs"},"region":{"startLine":360}}}],"partialFingerprints":{"codehealthFindingId/v1":"68351a90614f03ee04bcd6a830c08085ac56558ee3a5572fb232f816f6d1dac8"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: ModelPicker.ModelPicker: FunctionTooLong \u2014 ModelPicker runs 142 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 42 over it, 1.42\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":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"c16bde65215586c67eff88f3f5463cb4b0a7f3e942153f6e6afcc61f646a337b"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/edit.rs: FileTooLong \u2014 705 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), about 94% of them inside a single declaration: Edit (2 blocks, 31-905). The bar is 500 significant lines; this is 205 over it, 1.41\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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"32571b8f0e5d9ebaf5f642f9f2b822ad82fa9f16e803aad4a023c387665a981c"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/compositor.rs: FileTooLong \u2014 696 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), about 66% of them inside a single declaration: GpuCompositor (2 blocks, 140-715). The bar is 500 significant lines; this is 196 over it, 1.39\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":"crates/koharu-rasterizer/src/compositor.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f7667b7c00d5ddafa733f63e2713244cfc5c4c5bb5d9d14bcd722144dd31bf6e"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: FontPicker.FontPicker: FunctionTooLong \u2014 FontPicker runs 134 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 34 over it, 1.34\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":"packages/koharu/components/controls/FontPicker.tsx"},"region":{"startLine":50}}}],"partialFingerprints":{"codehealthFindingId/v1":"7baae65a31113cb8d64664c3e376ab2fc1ba324e9f440a7b0cd38536a8daa88c"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Path: TooManyMethods \u2014 40 methods. The bar is 30 methods; this is 10 over it, 1.33\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":"crates/koharu-torch/src/nn/var_store.rs"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"aa7c3d11fa647578909b79317c6276a016b4d34e9c976949e05a94adc2b469fa"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/prepared.rs: FileTooLong \u2014 655 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 155 over it, 1.31\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-rasterizer/src/prepared.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1855ebb60b770456e3f97d961e7efed5f47fc59ea0d90320935b516ab24436c9"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: stages/inpainting.rs: FileTooLong \u2014 651 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 151 over it, 1.30\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"fa6566b69172f6826ea9882d6f9a1ec0dc2ebcd170c66d7b23a311fb6df32fa8"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: KoharuLayoutRFDetrImageProcessor.postprocess: MethodTooLong \u2014 postprocess runs 129 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 29 over it, 1.29\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":"crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/processor.rs"},"region":{"startLine":75}}}],"partialFingerprints":{"codehealthFindingId/v1":"dc321ed4d4b7a847f97ec208a79a31c15bb72a2df2cd816ff3e65f2999c3035a"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: calendar.Calendar: FunctionTooLong \u2014 Calendar runs 128 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 28 over it, 1.28\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":"packages/ui/src/components/calendar.tsx"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"286d503be2822db5e593ce7c25a8663ff401095cbe798bfbc2237d912b01f947"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: recognizer/model.rs: FileTooLong \u2014 634 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 134 over it, 1.27\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f65ef1c07caacf0089a1f162a23442b01c7d3c01a04fd6244c697f7c5353f8c3"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: Updater.Updater: FunctionTooLong \u2014 Updater runs 126 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 26 over it, 1.26\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":"packages/koharu/components/app/Updater.tsx"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"4dd3b1d3b26158aa4073132dc4e941aa0c65d2ed9e87ad1519c25f64f0331ec4"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: RTDetrV2Model.new: MethodTooLong \u2014 new runs 125 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 25 over it, 1.25\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":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":111}}}],"partialFingerprints":{"codehealthFindingId/v1":"4f30af38e1f63496cb0d52ac7024d7e40438e4da2fa4dccddee56d54b8b5dde6"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: PPDocLayoutV3Model.new: MethodTooLong \u2014 new runs 124 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 24 over it, 1.24\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":"crates/koharu-ml/src/pp_doclayout_v3/model.rs"},"region":{"startLine":90}}}],"partialFingerprints":{"codehealthFindingId/v1":"fdf562815546e4e0e0fec16b5dd127b26079981d196f2421ca1cfae9acc7e836"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: TranslationPreferences.TranslationPreferences: FunctionTooLong \u2014 TranslationPreferences runs 122 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 22 over it, 1.22\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":"packages/koharu/components/preferences/TranslationPreferences.tsx"},"region":{"startLine":32}}}],"partialFingerprints":{"codehealthFindingId/v1":"d873f3cfdd47bd5d78041565bc8d2135d814de75788d6f547e72f094c5bd9e3f"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: llm/model.rs: FileTooLong \u2014 608 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), about 60% of them inside a single declaration: Model (3 blocks, 42-581). The bar is 500 significant lines; this is 108 over it, 1.22\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":"crates/koharu-ml/src/llm/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"860525b53e900f2827e38e149dd530f41d12e780f5c483e8de3155943fcd8762"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: comic_layout_yolo26s/model.rs: FileTooLong \u2014 602 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 102 over it, 1.20\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"cf5be22cc54ee322a49d75a528f1a4cade9bcfca22e149d93c8455b2bee8e0da"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: Inspector.LayerEditor: FunctionTooLong \u2014 LayerEditor runs 120 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 20 over it, 1.20\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":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":771}}}],"partialFingerprints":{"codehealthFindingId/v1":"127dbec277e63c73ef0f567ce10132523fce2d9e6b4c6f73d692a3aafc1c24c4"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Operation.apply: MethodTooLong \u2014 apply runs 119 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 19 over it, 1.19\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":"crates/koharu-scene/src/patch.rs"},"region":{"startLine":76}}}],"partialFingerprints":{"codehealthFindingId/v1":"fd27a29ca1f1d92ddef856e33ea15debe3e033a54f1a695c142ad6c058e0e4cf"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: Inspector.LayersInspector: FunctionTooLong \u2014 LayersInspector runs 119 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 19 over it, 1.19\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":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":493}}}],"partialFingerprints":{"codehealthFindingId/v1":"feb417932b4cca18bb556d3fcd706ddca1c6007e285ea3c07275e9ad2473187b"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: hayai_ocr/model.rs: FileTooLong \u2014 587 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 87 over it, 1.17\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/hayai_ocr/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"42040274d936b2f273c7f40be695c4cb4eb68ab8c3fd104a639f2e71ba288799"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: OutputPicker.OutputPicker: FunctionTooLong \u2014 OutputPicker runs 117 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 17 over it, 1.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":"packages/koharu/components/controls/OutputPicker.tsx"},"region":{"startLine":24}}}],"partialFingerprints":{"codehealthFindingId/v1":"dd4dda9f6478c2d52d7f0fb74d17d9e24cc381e800a3e9b94a8cae0574d34796"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: Inspector.LayerRow: FunctionTooLong \u2014 LayerRow runs 117 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 17 over it, 1.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":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":643}}}],"partialFingerprints":{"codehealthFindingId/v1":"83d2fcd41a8661a96a4249865302da50bfd2814af4bbbe72574b9f5e3a3fb98d"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: wrappers/jit.rs: FileTooLong \u2014 582 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 82 over it, 1.16\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-torch/src/wrappers/jit.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd43f55b0fa873de84fcb5013c71446967b83a237eabc2f97339071e8dee1ad3"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Flux2KleinInpaint.inference: MethodTooLong \u2014 inference runs 115 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) 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":"crates/koharu-ml/src/flux2_klein/mod.rs"},"region":{"startLine":149}}}],"partialFingerprints":{"codehealthFindingId/v1":"0c686a1ba7b87b71a09881b195088638ab861afd7dcae3278c29fbceaecc64c9"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: LlamaModel: ClassTooLong \u2014 457 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 44 methods, 4 blocks, lines 26-1028. 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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"cc87930a2606789fd64c567ffe3ae400f5c6c02ee59282dbd943060fa58c9c5d"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: GpuCompositor: ClassTooLong \u2014 457 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 17 methods, 2 blocks, lines 140-715. 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":"crates/koharu-rasterizer/src/compositor.rs"},"region":{"startLine":140}}}],"partialFingerprints":{"codehealthFindingId/v1":"e1588de1ac527c3b39ebbf1abe4709493b135ae65ec11d9a311a9c5ff24e3178"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: useCanvas.useCanvas: FunctionTooLong \u2014 useCanvas 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":"packages/koharu/components/editor/useCanvas.ts"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"b40bbdae0faaf4e0f8fc30f13f333c2e3e049280fd2773ee70af4e58cc53ec84"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/model.rs: FileTooLong \u2014 566 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), about 81% of them inside a single declaration: LlamaModel (4 blocks, 26-1028). The bar is 500 significant lines; this is 66 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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"b5965bef0a33b6289b85e3dbe6715c6ef5e168c3fe5a8ca3ebddbd79b75ea15b"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: VideoGenerationParams.to_native: MethodTooLong \u2014 to_native runs 113 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 13 over it, 1.13\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":"crates/koharu-diffusion/src/params.rs"},"region":{"startLine":991}}}],"partialFingerprints":{"codehealthFindingId/v1":"50beba8ee9830ef5f8a63a2302965bfc5e61aada8f06c98098f6d3c5d51db963"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: SelectionControls.SelectionControls: FunctionTooLong \u2014 SelectionControls runs 113 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 13 over it, 1.13\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":"packages/koharu/components/editor/SelectionControls.tsx"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"cf85e8fa65aeae413ae37cc69020874a2055704f9bb9c5d8ee932d6fb62815aa"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Traversal.text_draft: MethodTooLong \u2014 text_draft runs 112 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 12 over it, 1.12\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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":714}}}],"partialFingerprints":{"codehealthFindingId/v1":"1230bc394639b78657a604d99fdd12275d43f1958bb8b65f05efbe0ec1fcf2ce"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: koharu_psd::document::build: FunctionTooLong \u2014 koharu_psd::document::build runs 111 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 11 over it, 1.11\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":"crates/koharu-psd/src/document.rs"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"a39df161894aff9c9e009f13be32397f0e7eb32dacf7b83854cfbcd34605480d"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: Snapshot: TooManyMethods \u2014 33 methods, declared across 2 files: src/snapshot.rs (28), src/document.rs (5). The bar is 30 methods; this is 3 over it, 1.10\u00D7 the bar. That list is where to read them, not a suggestion to split the file: the members belong to the type wherever they are declared, so moving them between files leaves the count unchanged. 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":"crates/koharu-scene/src/snapshot.rs"},"region":{"startLine":12}}}],"partialFingerprints":{"codehealthFindingId/v1":"cbd14d11d37480fc6651430fc18b5c448f8169e581c8ce8ed59ee17e5d74ed9c"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: PPDocLayoutV3Model.forward: MethodTooLong \u2014 forward runs 110 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 10 over it, 1.10\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":"crates/koharu-ml/src/pp_doclayout_v3/model.rs"},"region":{"startLine":243}}}],"partialFingerprints":{"codehealthFindingId/v1":"91e70e34fe8336e63b7b88db57a608b800d6fcea73fabcbb9f05870dcb2dfde1"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: TextLayout.append_balanced_segment_lines: MethodTooLong \u2014 append_balanced_segment_lines runs 110 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 10 over it, 1.10\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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":955}}}],"partialFingerprints":{"codehealthFindingId/v1":"f0949dc3cb9f821317d252363111decfc7a855f2c01498bace70d9a0b2685641"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: components/sidebar.tsx: FileTooLong \u2014 545 significant lines (blank, comment-only and punctuation-only lines excluded). The bar is 500 significant lines; this is 45 over it, 1.09\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/sidebar.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"a4a72c485d5f91704beb21f74ae7d011c6ed27357413a35cbbb891daee98143b"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: ContextParams.to_native: MethodTooLong \u2014 to_native runs 109 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) 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":"crates/koharu-diffusion/src/params.rs"},"region":{"startLine":181}}}],"partialFingerprints":{"codehealthFindingId/v1":"893f9053748aff9452cd93558f25de0c0ce1fd03887263de45715f714a5a6183"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: PPDocLayoutV3ImageProcessor.postprocess_batch: MethodTooLong \u2014 postprocess_batch runs 109 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) 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":"crates/koharu-ml/src/pp_doclayout_v3/processor.rs"},"region":{"startLine":128}}}],"partialFingerprints":{"codehealthFindingId/v1":"7cd3e2b938021ec35ac54962d480161fd73bbaa01777565e143799cddadc6e48"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: GpuCompositor.render: MethodTooLong \u2014 render runs 109 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) 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":"crates/koharu-rasterizer/src/compositor.rs"},"region":{"startLine":389}}}],"partialFingerprints":{"codehealthFindingId/v1":"fcdd346274e0f92b218fd9da0860c5a7cf6e9f1b6b742581a82f2e183a677974"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: TypesettingPreferences.TypesettingPreferences: FunctionTooLong \u2014 TypesettingPreferences 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":"packages/koharu/components/preferences/TypesettingPreferences.tsx"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"8902153955f88288dd4b2c682ea36cda13f0cacd0490bc0cbe324b5cd2d8e386"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: src/bubble.rs: FileTooLong \u2014 541 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 41 over it, 1.08\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-renderer/src/bubble.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"d14e5964faabf2e90713a3540662c274460abf6dadbc7b5de1fe69fd83b769c4"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Traversal.visit_children: MethodTooLong \u2014 visit_children runs 108 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) in one body. The bar is 100 significant lines; this is 8 over it, 1.08\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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":584}}}],"partialFingerprints":{"codehealthFindingId/v1":"850d343a0f9ea34cf140693236c64e76c6bbaa6bee9840aa615b5c5754c36d71"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: speech_bubble_yolov8m/model.rs: FileTooLong \u2014 537 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 37 over it, 1.07\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolov8m/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"eaf27b62ef8af99425b28a32b96078c79dc3785a9f2e287802ed1a042b4a2c3d"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: WebCanvas: ClassTooLong \u2014 427 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 24 methods, 4 blocks, lines 1089-1754. The bar is 400 significant lines; this is 27 over it, 1.07\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":"crates/koharu-canvas/src/web.rs"},"region":{"startLine":1089}}}],"partialFingerprints":{"codehealthFindingId/v1":"166bfb70bb422b280de7868ac6ca9a888b394922b9a8b17027f20d57075108d5"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: State: TooManyMethods \u2014 32 methods. The bar is 30 methods; this is 2 over it, 1.07\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":"crates/koharu-scene/src/state.rs"},"region":{"startLine":440}}}],"partialFingerprints":{"codehealthFindingId/v1":"aadec91cbe69f909a152df7a5e6c003d1ea17a86b60c666a96dadc26802b348e"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: speech_bubble_yolo11n/model.rs: FileTooLong \u2014 533 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 33 over it, 1.07\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/model.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"6a7f53e4a8375828ffa071ecd9c273834ea2e6f295433952ff58938440a267d5"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: State: ClassTooLong \u2014 417 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 32 methods, 2 blocks, lines 440-967. The bar is 400 significant lines; this is 17 over it, 1.04\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":"crates/koharu-scene/src/state.rs"},"region":{"startLine":440}}}],"partialFingerprints":{"codehealthFindingId/v1":"e5c52188f3f3583033219ff71c7cdc363dd99bee540f2e64e0ab318ac6e70dbb"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: LlamaSampler: 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":"crates/koharu-llama/src/sampling.rs"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"2774cd2326da2e8d9e12cec558a96620739d7d226196e83b64121d2cadbde904"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: agent/host.rs: FileTooLong \u2014 516 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), about 64% of them inside a single declaration: KoharuHost (3 blocks, 27-457). The bar is 500 significant lines; this is 16 over it, 1.03\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":"crates/koharu-app/src/commands/agent/host.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"65dd74638d57a2f81a9aa4d6400c45609ff4192b874fcdd3642b557b85341861"}},{"ruleId":"D3","level":"warning","message":{"text":"FunctionTooLong: GenerationPreferences.GenerationPreferences: FunctionTooLong \u2014 GenerationPreferences runs 103 significant lines (blank, comment-only and punctuation-only lines excluded) in one body. The bar is 100 significant lines; this is 3 over it, 1.03\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":"packages/koharu/components/preferences/GenerationPreferences.tsx"},"region":{"startLine":13}}}],"partialFingerprints":{"codehealthFindingId/v1":"1bb906e71ffcbb2bd4ae84e9bc47bf8d0ab20a22108fc3b878200fe71e75283a"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: detector/processor.rs: FileTooLong \u2014 511 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 11 over it, 1.02\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f8edb75795ea0eef3ea4ece5df8090493efdc5d491516ce27ffd894c5f19d1bb"}},{"ruleId":"D3","level":"warning","message":{"text":"ClassTooLong: Tensor: ClassTooLong \u2014 404 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted), 5422 methods, 6 blocks, lines 17-856. The bar is 400 significant lines; this is 4 over it, 1.01\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":"crates/koharu-torch/src/wrappers/tensor.rs"},"region":{"startLine":17}}}],"partialFingerprints":{"codehealthFindingId/v1":"e69528e3d04aa3dad7903f61cebc7c7dbebc26d98293316d9910e20260b30752"}},{"ruleId":"D3","level":"warning","message":{"text":"MethodTooLong: Project.apply_raster_stroke: MethodTooLong \u2014 apply_raster_stroke runs 101 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted) 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":"crates/koharu-app/src/commands/project.rs"},"region":{"startLine":741}}}],"partialFingerprints":{"codehealthFindingId/v1":"72bc112e31661a45b3d33613c7621ab17d4378e76ec9a87fa2071de252abf379"}},{"ruleId":"D3","level":"warning","message":{"text":"FileTooLong: pp_ocr_v6/pp_lcnet_v4.rs: FileTooLong \u2014 501 significant lines (blank, comment-only and punctuation-only lines excluded, and inline test code \u2014 #[cfg(test)] modules and bare #[test] functions \u2014 not counted). The bar is 500 significant lines; this is 1 over it, 1.00\u00D7 the bar. To reduce it, split the file along the responsibilities already in it: move each cohesive group of declarations into its own sibling file in the same module or package, so no one file has to be read whole to change one of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/pp_ocr_v6/pp_lcnet_v4.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"947dc8daf72cc0e2f04a9ebc25bd5fa78ee801466bef445201d8847619f06b4f"}},{"ruleId":"D4","level":"warning","message":{"text":"Near-duplicate member pair (52 shared lines): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:605-668 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:559-619 \u2014 These two members are variants of one another: 52 of their lines are already reported as duplicated blocks below, spread through both bodies rather than gathered into one. Read them as a single construct written twice. The repair is at the members\u0027 grain \u2014 factor the shared pipeline into one implementation the two call with their differences as parameters or as an injected step, or, where the difference is systematic (sync against async, one transport against another), generate one from the other. Extracting the individual blocks below is not the same fix: it leaves the two bodies in place and the next edit still has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":605}}}],"partialFingerprints":{"codehealthFindingId/v1":"fed681b727a1ba2eee7691b752d9c41f0bd96c02410c1778a3e4e048185144ca"}},{"ruleId":"D4","level":"warning","message":{"text":"Near-duplicate member pair (46 shared lines): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:110-214 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:103-205 \u2014 These two members are variants of one another: 46 of their lines are already reported as duplicated blocks below, spread through both bodies rather than gathered into one. Read them as a single construct written twice. The repair is at the members\u0027 grain \u2014 factor the shared pipeline into one implementation the two call with their differences as parameters or as an injected step, or, where the difference is systematic (sync against async, one transport against another), generate one from the other. Extracting the individual blocks below is not the same fix: it leaves the two bodies in place and the next edit still has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":110}}}],"partialFingerprints":{"codehealthFindingId/v1":"984e0f4b0f0275056f2870d583e3c2d3ae04a81bbb9b4350ad407163abba666d"}},{"ruleId":"D4","level":"warning","message":{"text":"Near-duplicate member pair (42 shared lines): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:500-555 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:449-505 \u2014 These two members are variants of one another: 42 of their lines are already reported as duplicated blocks below, spread through both bodies rather than gathered into one. Read them as a single construct written twice. The repair is at the members\u0027 grain \u2014 factor the shared pipeline into one implementation the two call with their differences as parameters or as an injected step, or, where the difference is systematic (sync against async, one transport against another), generate one from the other. Extracting the individual blocks below is not the same fix: it leaves the two bodies in place and the next edit still has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":500}}}],"partialFingerprints":{"codehealthFindingId/v1":"bdffeef6f735495a0382e5369fd14ee1f1d7a970bc948bf286c5c48b7ed9ed46"}},{"ruleId":"D4","level":"warning","message":{"text":"Near-duplicate member pair (23 shared lines): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:99-188 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:110-214 \u2014 These two members are variants of one another: 23 of their lines are already reported as duplicated blocks below, spread through both bodies rather than gathered into one. Read them as a single construct written twice. The repair is at the members\u0027 grain \u2014 factor the shared pipeline into one implementation the two call with their differences as parameters or as an injected step, or, where the difference is systematic (sync against async, one transport against another), generate one from the other. Extracting the individual blocks below is not the same fix: it leaves the two bodies in place and the next edit still has to be made twice."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":99}}}],"partialFingerprints":{"codehealthFindingId/v1":"2711e41146e722976db302e6b547240cd0f871ecfd2f3996c2795132b7e633c4"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (26 corresponding lines): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:811-836 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:706-731 \u2014 These two members are one piece of code written twice and then edited apart: 26 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":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":811}}}],"partialFingerprints":{"codehealthFindingId/v1":"6fd98f0b68115a0f089e4cb55682640259ed0417a4c272c158346a83f783737e"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (23 corresponding lines): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:562-584 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:502-524 \u2014 These two members are one piece of code written twice and then edited apart: 23 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":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":562}}}],"partialFingerprints":{"codehealthFindingId/v1":"42bbd9883a95fd9dfd9984065e2564e260bf20d44ea2538064ab36a47820b375"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (24 corresponding lines): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:330-353 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:274-297 \u2014 These two members are one piece of code written twice and then edited apart: 24 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":330}}}],"partialFingerprints":{"codehealthFindingId/v1":"8988ae781d498bde83c0c39a53bd1991e729aef3c33e5c2116fe72143e2d38c4"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (11 corresponding lines): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:420-430 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:422-432 \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":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":420}}}],"partialFingerprints":{"codehealthFindingId/v1":"98a59ab0f1c0d68739c58a2182d181923d32c63e43d5d3846c9b2199e74dd7ef"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (17 corresponding lines): crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs:86-102 | crates/koharu-ml/src/bin/speech_bubble_yolov8m.rs:85-101 \u2014 These two members are one piece of code written twice and then edited apart: 17 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":"crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"bb5e7bd966a9faeae0b1e01dcd938346d2921d515f382fcef836979e5438d6fd"}},{"ruleId":"D4","level":"warning","message":{"text":"Edited copy of a member (16 corresponding lines): crates/koharu-ml/src/bin/comic_layout_yolo26s.rs:149-170 | crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs:66-81 \u2014 These two members are one piece of code written twice and then edited apart: 16 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":"crates/koharu-ml/src/bin/comic_layout_yolo26s.rs"},"region":{"startLine":149}}}],"partialFingerprints":{"codehealthFindingId/v1":"2f6fdfc63e00e2c75b16ca194ae5b7590ffe6ece59150b6b5f1a7a34fb6fc781"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (5 members, 50\u002B identical tokens): crates/koharu-llama/src/context/kv_cache.rs:57-67 | crates/koharu-llama/src/context/kv_cache.rs:88-100 | crates/koharu-llama/src/context/kv_cache.rs:143-153 | crates/koharu-llama/src/context/kv_cache.rs:179-189 | crates/koharu-llama/src/context/kv_cache.rs:213-225 \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":"crates/koharu-llama/src/context/kv_cache.rs"},"region":{"startLine":57}}}],"partialFingerprints":{"codehealthFindingId/v1":"ff7a7d3e9953989ab47483863d10b1d5b213d2e6ea4c79731d8f4dcc011b9ed8"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (5 members, 50\u002B identical tokens): crates/koharu-translator/src/remote/deepseek.rs:53-106 | crates/koharu-translator/src/remote/lm_studio.rs:39-99 | crates/koharu-translator/src/remote/openai.rs:79-139 | crates/koharu-translator/src/remote/openai_compatible.rs:39-103 | crates/koharu-translator/src/remote/openrouter.rs:30-98 \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":"crates/koharu-translator/src/remote/deepseek.rs"},"region":{"startLine":53}}}],"partialFingerprints":{"codehealthFindingId/v1":"f034ef2801288d2476771730819cc4f25f9d349daefe486bc05a0a8650020e73"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (4 members, 50\u002B identical tokens): crates/koharu-ml/src/aot_inpainting/processor.rs:196-208 | crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:372-384 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:432-444 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:434-447 \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":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":196}}}],"partialFingerprints":{"codehealthFindingId/v1":"d5bc3e846a4204985a519b69a9d84d0825bac96a95d2dc9852cf548ebdac78c5"}},{"ruleId":"D4","level":"warning","message":{"text":"Members sharing a duplicated core (4 members, 50\u002B identical tokens): crates/koharu-ml/src/flux2_klein/mod.rs:156-290 | crates/koharu-ml/src/flux2_klein/processor.rs:70-88 | crates/koharu-ml/src/flux2_klein/processor.rs:165-197 | crates/koharu-translator/src/backend.rs:65-91 \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":"crates/koharu-ml/src/flux2_klein/mod.rs"},"region":{"startLine":156}}}],"partialFingerprints":{"codehealthFindingId/v1":"9e498a5b391488745b287e234b13f87b1f4ae321785f3527b914b909a4f03503"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (40\u201342 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:564-603 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:516-557 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":564}}}],"partialFingerprints":{"codehealthFindingId/v1":"4c545f5cd33a481b2d43daf7a3e96e3c68b73554fc417b190fbe72b6db76135f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (33 lines \u00D7 2): crates/koharu-translator/src/remote/lm_studio.rs:50-82 | crates/koharu-translator/src/remote/openai_compatible.rs:50-82 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai_compatible.rs\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 140 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":"crates/koharu-translator/src/remote/lm_studio.rs"},"region":{"startLine":50}}}],"partialFingerprints":{"codehealthFindingId/v1":"158160bcab1f94891de678309b3cffbd7a13b04a58c24053d954f2ad0fc393d1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (31 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:435-465 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:380-410 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":435}}}],"partialFingerprints":{"codehealthFindingId/v1":"e3ef0b60179fe273cc7870aa3c5abcf268b4768937afe6a014f9250290ecfef4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (28\u201329 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:205-232 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:231-259 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":205}}}],"partialFingerprints":{"codehealthFindingId/v1":"7f3e7783aafc7060bccd37c51b7d41f1d9aeb977c4fc470236f7120c4ff58178"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (27 lines \u00D7 3): crates/koharu-translator/src/remote/lm_studio.rs:43-69 | crates/koharu-translator/src/remote/openai_compatible.rs:43-69 | crates/koharu-translator/src/remote/openrouter.rs:35-61 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai_compatible.rs\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 140 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":"crates/koharu-translator/src/remote/lm_studio.rs"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"d395950f3d66dd4823221ddff7db03c5342f3666091e392b3f40353b6de52962"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (27 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1150-1176 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1320-1346 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1150}}}],"partialFingerprints":{"codehealthFindingId/v1":"105b10deaa4db5ea2cef8164b09d0221bff6fb915a5d45108b6c7dbc4576ed45"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (27 lines \u00D7 2): crates/koharu-runtime/src/runtime/packages/diffusion.rs:126-152 | crates/koharu-runtime/src/runtime/packages/llama.rs:126-152 \u2014 before extracting anything, compare \u0060crates/koharu-runtime/src/runtime/packages/diffusion.rs\u0060 and \u0060crates/koharu-runtime/src/runtime/packages/llama.rs\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 56 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":"crates/koharu-runtime/src/runtime/packages/diffusion.rs"},"region":{"startLine":126}}}],"partialFingerprints":{"codehealthFindingId/v1":"ac7e6fad692f37f805ca790cd218af8d6e663fbbb1ce4cad10afa2d1cc9de3b7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (26 lines \u00D7 2): crates/koharu-llama/src/model.rs:448-473 | crates/koharu-llama/src/model.rs:550-575 \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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":448}}}],"partialFingerprints":{"codehealthFindingId/v1":"46c2142ce857266dc6402eca31040951c0fb2c1e7fd470d96c1d75ecd6339089"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (26 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:924-949 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1050-1075 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":924}}}],"partialFingerprints":{"codehealthFindingId/v1":"5f9e5b03ffce510fca00f6a320215445f0b4f46b55093233769e3c2045c2e443"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (25 lines \u00D7 4): crates/koharu-translator/src/remote/lm_studio.rs:42-66 | crates/koharu-translator/src/remote/openai.rs:82-106 | crates/koharu-translator/src/remote/openai_compatible.rs:42-66 | crates/koharu-translator/src/remote/openrouter.rs:34-58 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai.rs\u0060 as WHOLE FILES: this scan already matched 3 separate duplicated blocks between them, totalling at least 63 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":"crates/koharu-translator/src/remote/lm_studio.rs"},"region":{"startLine":42}}}],"partialFingerprints":{"codehealthFindingId/v1":"713769d322e99c9c226c0dd55d52514304056b38456f61d99e256ac43fe05764"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 5): crates/koharu-translator/src/remote/deepseek.rs:55-78 | crates/koharu-translator/src/remote/lm_studio.rs:41-64 | crates/koharu-translator/src/remote/openai.rs:81-104 | crates/koharu-translator/src/remote/openai_compatible.rs:41-64 | crates/koharu-translator/src/remote/openrouter.rs:33-56 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai.rs\u0060 as WHOLE FILES: this scan already matched 3 separate duplicated blocks between them, totalling at least 63 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":"crates/koharu-translator/src/remote/deepseek.rs"},"region":{"startLine":55}}}],"partialFingerprints":{"codehealthFindingId/v1":"a16b6d3d3bec99928f7f65abbc00be46f1dd127c5a31ad3d265480a4077747cb"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs:41-64 | crates/koharu-ml/src/bin/speech_bubble_yolov8m.rs:42-65 \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":"crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs"},"region":{"startLine":41}}}],"partialFingerprints":{"codehealthFindingId/v1":"afaf314496f91478720094ee6778fd945739ef4926b79bc210da59dea1ef9eac"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:68-91 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:68-91 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":68}}}],"partialFingerprints":{"codehealthFindingId/v1":"776d54c5997796c4480cb8bb7bfb87f1a513636caf5fbf5d00de5969b6c1b0e0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:402-425 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:346-369 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":402}}}],"partialFingerprints":{"codehealthFindingId/v1":"53ea16365024aef2fb908179ede8b1d0fdf812126357cdb75186bb9a099905e7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1508-1531 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1903-1926 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1508}}}],"partialFingerprints":{"codehealthFindingId/v1":"7b916e337974e281b10c1e616ab11b0a4e0ec570e715e7832233b0c68704d748"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1674-1697 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2161-2184 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1674}}}],"partialFingerprints":{"codehealthFindingId/v1":"73d23d9b368e4d415ba0733adeecc7997fa97e15cc52d8309ef307c7b2b71ced"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (24 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:127-150 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:120-143 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 10 separate duplicated blocks between them, totalling at least 131 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":127}}}],"partialFingerprints":{"codehealthFindingId/v1":"e9a231334584d4335d0db5ae2ef57501aa8af877b5d242387da5bf15641939e3"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (21\u201323 lines \u00D7 3): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:50-70 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:60-80 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:51-73 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":50}}}],"partialFingerprints":{"codehealthFindingId/v1":"63fb423bf89a783f9cfcf4e4ca6dc436706858e2b63e92a0af7093516215834b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (23 lines \u00D7 3): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:375-397 | crates/koharu-ml/src/comic_text_detector/processor.rs:472-494 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:319-341 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/comic_text_detector/processor.rs\u0060 as WHOLE FILES: this scan already matched 3 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":375}}}],"partialFingerprints":{"codehealthFindingId/v1":"940c25ac8e961c51b08f7eadbc98346654287493d16ccbb1ad1cce3af6d45fcd"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (23 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1115-1137 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1287-1309 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1115}}}],"partialFingerprints":{"codehealthFindingId/v1":"e6462f4c04290a835fba0e728f97ca17f63be2b6309932a58ad12c339869849f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (23 lines \u00D7 2): crates/koharu-torch/src/wrappers/kind.rs:35-57 | crates/koharu-torch/src/wrappers/kind.rs:86-108 \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":"crates/koharu-torch/src/wrappers/kind.rs"},"region":{"startLine":35}}}],"partialFingerprints":{"codehealthFindingId/v1":"7fb1776f771046b8fa9cbe98e990771c9611dc80ac0a579912c50ed58cc1a767"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (22 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:630-651 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:581-602 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":630}}}],"partialFingerprints":{"codehealthFindingId/v1":"e73b5a33d687adc348c43514bbdf6dc8a5b262e8b135620b57ffc68a558bd2d7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (21 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:72-92 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:82-102 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":72}}}],"partialFingerprints":{"codehealthFindingId/v1":"89b432d1170fded9461616ed7f429a13c36b53a606d42b1161ccf31abc86f563"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (21 lines \u00D7 2): crates/koharu-ml/src/comic_text_detector/model.rs:176-196 | crates/koharu-ml/src/comic_text_detector/model.rs:226-246 \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":"crates/koharu-ml/src/comic_text_detector/model.rs"},"region":{"startLine":176}}}],"partialFingerprints":{"codehealthFindingId/v1":"d0c68917d44a22b2b2877b1e96f1e2d00060cc146a23c3f6353153a4ae09c2ad"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (20 lines \u00D7 2): crates/koharu-llama/src/context/session.rs:214-233 | crates/koharu-llama/src/context/session.rs:312-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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-llama/src/context/session.rs"},"region":{"startLine":214}}}],"partialFingerprints":{"codehealthFindingId/v1":"48fe91b29b28af3ecb33310dbc6ec3b4d271a7d39e783ede8f54fb7b55be7324"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (20 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:334-353 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:278-297 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":334}}}],"partialFingerprints":{"codehealthFindingId/v1":"d7d5cd68b3e4c23585261a82e1eb8f001eef2ad4ad2381bf8d51a664e75e7353"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (19 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:865-883 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:990-1008 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":865}}}],"partialFingerprints":{"codehealthFindingId/v1":"e8f0e03f74575e3995adf518dfff1dfcafba6a8226bcecc50056a0460068d731"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (18 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:819-836 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:714-731 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":819}}}],"partialFingerprints":{"codehealthFindingId/v1":"54ea0de51089db45bb268f835004254c83291741ca5b8f19c423ad8677498db4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (18 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1369-1386 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1587-1604 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1369}}}],"partialFingerprints":{"codehealthFindingId/v1":"7d03643f54531a359be3aeabc8822a7812f6e7ae3ab602a82cf35bc52af1efba"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (18 lines \u00D7 2): crates/koharu-ml/src/paddle_ocr_vl/model.rs:143-160 | crates/koharu-ml/src/paddle_ocr_vl/model.rs:198-215 \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":"crates/koharu-ml/src/paddle_ocr_vl/model.rs"},"region":{"startLine":143}}}],"partialFingerprints":{"codehealthFindingId/v1":"3c7abe5ad292d5342ac8ef0b456a2786c1d8dc740f476f8088cfab097b65c338"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (18 lines \u00D7 2): crates/koharu-rasterizer/src/native.rs:297-314 | crates/koharu-rasterizer/src/native.rs:357-374 \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":"crates/koharu-rasterizer/src/native.rs"},"region":{"startLine":297}}}],"partialFingerprints":{"codehealthFindingId/v1":"e6de9cec2f550f96ce87e9947b13f92c6129ca4906195345d81084c3b9a6d4ad"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 3): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:356-372 | crates/koharu-ml/src/comic_text_detector/processor.rs:451-467 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:300-316 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/comic_text_detector/processor.rs\u0060 as WHOLE FILES: this scan already matched 3 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":356}}}],"partialFingerprints":{"codehealthFindingId/v1":"537a9df1b066506590f61cd47c51721a54ad101791e1f52fe928a024eea1afa0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): crates/koharu-llama/src/mtmd.rs:485-501 | crates/koharu-llama/src/mtmd.rs:538-554 \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":"crates/koharu-llama/src/mtmd.rs"},"region":{"startLine":485}}}],"partialFingerprints":{"codehealthFindingId/v1":"c8c8358ab17ce3bb5c20a7dccffb683416ff1e905fbde8d3cd330fd61c2dd69a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:325-341 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:332-348 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 10 separate duplicated blocks between them, totalling at least 131 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":325}}}],"partialFingerprints":{"codehealthFindingId/v1":"193935386452e1fdb49f79c587fea586e7a10c2892162b9ddb232ebd11f6f0c3"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): crates/koharu-torch/src/nn/rnn.rs:154-170 | crates/koharu-torch/src/nn/rnn.rs:244-260 \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":"crates/koharu-torch/src/nn/rnn.rs"},"region":{"startLine":154}}}],"partialFingerprints":{"codehealthFindingId/v1":"b9edb3b8b5e5508bfa57906a04a141832c91ad8afe5824477172b0918a9aba37"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): crates/koharu-llama/src/sampling.rs:320-335 | crates/koharu-llama/src/sampling.rs:358-373 \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":"crates/koharu-llama/src/sampling.rs"},"region":{"startLine":320}}}],"partialFingerprints":{"codehealthFindingId/v1":"8bb0deefe9f84b706fbfa7cd50431e293de76c13417765b612f7a8aa5811cf7b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:473-488 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:476-491 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":473}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c892f2b2b5d9b899e37392950d4afe9fa9ca0b29e058eb805655dba9364b613"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/config.rs:85-100 | crates/koharu-ml/src/pp_doclayout_v3/config.rs:72-87 \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":"crates/koharu-ml/src/comic_text_bubble_detector/config.rs"},"region":{"startLine":85}}}],"partialFingerprints":{"codehealthFindingId/v1":"b11de86f08f8d114210441304d1e88dce805c0fd691c6978210b5d849f74ba5e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1227-1242 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1389-1404 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1227}}}],"partialFingerprints":{"codehealthFindingId/v1":"305274ec973331a816a9dada575a6e215a9a4725f5f35c2506d4302d0eec4351"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2): crates/koharu-ml/src/flux2_klein/mod.rs:40-55 | crates/koharu-ml/src/flux2_klein/mod.rs:132-147 \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":"crates/koharu-ml/src/flux2_klein/mod.rs"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"f1fc50026dc2b8746725f32e27feabedf3102d6a7c5a732da1a1928c333e638d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:541-555 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:491-505 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":541}}}],"partialFingerprints":{"codehealthFindingId/v1":"801139718c7da7b724fafa53ee6639d682c09b420bb66d5ef7c56368a98af6e8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1714-1728 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2195-2209 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1714}}}],"partialFingerprints":{"codehealthFindingId/v1":"bae1d123986f91cd9872ac8e1d3e52da8c02257caf44e79733facf37cd69f501"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14\u201315 lines \u00D7 2): crates/koharu-ml/src/flux2_klein/processor.rs:124-137 | crates/koharu-ml/src/flux2_klein/processor.rs:141-155 \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":"crates/koharu-ml/src/flux2_klein/processor.rs"},"region":{"startLine":124}}}],"partialFingerprints":{"codehealthFindingId/v1":"10cddc3e0490789aac2227b926a0060514287508237bb29bd00a2121bf46f463"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:111-125 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:104-118 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 10 separate duplicated blocks between them, totalling at least 131 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":111}}}],"partialFingerprints":{"codehealthFindingId/v1":"7001383ca46e14002966442c4a8147b3c946b78d4d1d3f484582e35121049188"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 3): crates/koharu-translator/src/remote/lm_studio.rs:68-81 | crates/koharu-translator/src/remote/openai.rs:108-121 | crates/koharu-translator/src/remote/openai_compatible.rs:68-81 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai.rs\u0060 as WHOLE FILES: this scan already matched 3 separate duplicated blocks between them, totalling at least 63 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":"crates/koharu-translator/src/remote/lm_studio.rs"},"region":{"startLine":68}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca27accb884fba3d31d6cf0f68e200bcc3761183742521027b0dca896784a611"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13\u201314 lines \u00D7 2): crates/koharu-llama/src/model.rs:950-963 | crates/koharu-llama/src/model.rs:968-980 \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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":950}}}],"partialFingerprints":{"codehealthFindingId/v1":"9ec97d4e68665035fc9cbd4c87dd44e52692639c3e7baef23006de686cc36f87"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:501-514 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:450-463 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":501}}}],"partialFingerprints":{"codehealthFindingId/v1":"a1910c3904ed041159d75488729fdf390bb2de9fc702e3ec602ca03a7a64970b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10\u201314 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:298-307 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:276-289 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":298}}}],"partialFingerprints":{"codehealthFindingId/v1":"a7220849d88d4db618f9e9a0214e98d81e9f7ec49a5d169449693e9c35528afe"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2): crates/koharu-renderer/src/renderer.rs:851-864 | crates/koharu-renderer/src/renderer.rs:878-891 \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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":851}}}],"partialFingerprints":{"codehealthFindingId/v1":"24f6546b6fdc165693a66548d45794e2adaf499c4bac76205725f0079aa7b117"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:189-201 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:269-281 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":189}}}],"partialFingerprints":{"codehealthFindingId/v1":"9badc68b1fc70107564b18ebe33ee41bee7ddaed6d405f0d5598b6ef470dba1f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:298-310 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:191-203 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":298}}}],"partialFingerprints":{"codehealthFindingId/v1":"45320960aacf7700f435b79f7db7cfdcf551548b435c5198c33dde3a6b21895a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:518-530 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:467-479 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":518}}}],"partialFingerprints":{"codehealthFindingId/v1":"364e1a98600a002c3fbf44e133862c9705c79d14ef8523cdda40456f512b3fe1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12\u201313 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:326-338 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:315-326 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":326}}}],"partialFingerprints":{"codehealthFindingId/v1":"6e40fe6fc99276e2214db7cdc76fd2089840a7831d4f7d3d8dab4eaeb558e94c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12\u201313 lines \u00D7 2): crates/koharu-ml/src/pp_doclayout_v3/processor.rs:431-443 | crates/koharu-pipeline/src/stages/detection.rs:1330-1341 \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":"crates/koharu-ml/src/pp_doclayout_v3/processor.rs"},"region":{"startLine":431}}}],"partialFingerprints":{"codehealthFindingId/v1":"8b8198c090a9483395aa131b3273ed951062801591cde56bb4346ae6b7f5c606"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2): crates/koharu-runtime/src/hardware/hip/linux.rs:70-82 | crates/koharu-runtime/src/hardware/hip/windows.rs:81-93 \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":"crates/koharu-runtime/src/hardware/hip/linux.rs"},"region":{"startLine":70}}}],"partialFingerprints":{"codehealthFindingId/v1":"caf59d46155a568fe11974f325b70aa4ed1fc2d5748565e5cf5ed5861e86496f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 4): crates/koharu-ml/src/aot_inpainting/processor.rs:197-208 | crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:373-384 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:433-444 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:436-447 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":197}}}],"partialFingerprints":{"codehealthFindingId/v1":"0dc87c20036690ee7a3bcb3663fc6da50174ab1ec919d30ffbe407f0fad6e742"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-ml/src/baberu_ocr/model.rs:435-446 | crates/koharu-ml/src/hayai_ocr/model.rs:468-479 \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":"crates/koharu-ml/src/baberu_ocr/model.rs"},"region":{"startLine":435}}}],"partialFingerprints":{"codehealthFindingId/v1":"e1e6589c82fa69e01a5ffa58a9811b7df219506a15b8444e5fab12a774f6ca43"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:444-455 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:447-458 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":444}}}],"partialFingerprints":{"codehealthFindingId/v1":"a76201b65903363dc3e79eeee8e98c63edbd8ac6733409b91012736f0c72f01b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:565-576 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:505-516 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":565}}}],"partialFingerprints":{"codehealthFindingId/v1":"bf60babe003f3a809dc1d779c29318d6bc28333f01828d435adf7ff33904db6a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-ml/src/rorem_mixed/processor.rs:86-97 | crates/koharu-ml/src/rorem_mixed/processor.rs:150-161 \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":"crates/koharu-ml/src/rorem_mixed/processor.rs"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"d81acc86efb5781a08fbb42466d04a6feac38a340412e08ec86d135078f55749"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-rasterizer/src/native.rs:180-191 | crates/koharu-rasterizer/src/native.rs:235-246 \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":"crates/koharu-rasterizer/src/native.rs"},"region":{"startLine":180}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd7b78e24d4c9a08b6a98bc9714b96b13dde724e15e6d3ebf0cee9aefa3507c5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10\u201311 lines \u00D7 2): crates/koharu-app/src/commands/lifecycle.rs:188-198 | crates/koharu-app/src/commands/lifecycle.rs:338-347 \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":"crates/koharu-app/src/commands/lifecycle.rs"},"region":{"startLine":188}}}],"partialFingerprints":{"codehealthFindingId/v1":"52ebde583ae351c1a2fb237e3c95a269465d8bb83c8ab5f3f1e71faf5f7b9861"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-ml/src/bin/comic_text_detector.rs:47-57 | crates/koharu-ml/src/bin/pp_doclayout_v3.rs:45-56 \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":"crates/koharu-ml/src/bin/comic_text_detector.rs"},"region":{"startLine":47}}}],"partialFingerprints":{"codehealthFindingId/v1":"0f5df9a4d4737f96cf3c66a9bf4fd29822f32e3d9a15fa555c4e779672780085"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:360-370 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:408-418 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":360}}}],"partialFingerprints":{"codehealthFindingId/v1":"f0f61d44e479a295e6954f50b1fff35ce373139b2a870213c2e361317f93dfb3"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:191-201 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:158-168 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":191}}}],"partialFingerprints":{"codehealthFindingId/v1":"9506259e75eaeec22a0873c6ad1b918c94f4ff8cf0891f33637c9846104c42ff"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:808-818 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:919-929 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":808}}}],"partialFingerprints":{"codehealthFindingId/v1":"85d3f774cfd4c6e195b5e5faa3b22b52a12bd24c8ff35f6a3c015b8e30cd20dc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/mod.rs:48-58 | crates/koharu-ml/src/speech_bubble_yolov8m/mod.rs:48-58 \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":"crates/koharu-ml/src/speech_bubble_yolo11n/mod.rs"},"region":{"startLine":48}}}],"partialFingerprints":{"codehealthFindingId/v1":"0595af11a0fa108179945eea0be0447d2eb323f5ac796a93739792d9984f3af0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-scene/src/state.rs:56-66 | crates/koharu-scene/src/state.rs:426-436 \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":"crates/koharu-scene/src/state.rs"},"region":{"startLine":56}}}],"partialFingerprints":{"codehealthFindingId/v1":"7cbcb999eed2db7e075932b715bc4b87d11cb34b4a0d71cfb8732ea33cad1763"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 4): crates/koharu-ml/src/flux2_klein/mod.rs:185-194 | crates/koharu-ml/src/flux2_klein/processor.rs:76-85 | crates/koharu-ml/src/flux2_klein/processor.rs:178-187 | crates/koharu-translator/src/backend.rs:78-87 \u2014 there are 4 copies across 3 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 4 sites; resolving a subset leaves the remainder to drift apart."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/flux2_klein/mod.rs"},"region":{"startLine":185}}}],"partialFingerprints":{"codehealthFindingId/v1":"e2227f98e95c778b9057729d829d335d174ecb63944f2f39c8f8c423be32a9b1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 3): crates/koharu-runtime/src/runtime/packages/diffusion.rs:112-121 | crates/koharu-runtime/src/runtime/packages/llama.rs:112-121 | crates/koharu-runtime/src/runtime/packages/torch.rs:103-112 \u2014 before extracting anything, compare \u0060crates/koharu-runtime/src/runtime/packages/diffusion.rs\u0060 and \u0060crates/koharu-runtime/src/runtime/packages/llama.rs\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 56 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":"crates/koharu-runtime/src/runtime/packages/diffusion.rs"},"region":{"startLine":112}}}],"partialFingerprints":{"codehealthFindingId/v1":"54ee9c392b911e7bbd15354953c127f6e126e96ca1340b46c16f0cdb356f2bca"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): crates/koharu-app/src/commands/canvas.rs:161-170 | crates/koharu-app/src/commands/canvas.rs:193-202 \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":"crates/koharu-app/src/commands/canvas.rs"},"region":{"startLine":161}}}],"partialFingerprints":{"codehealthFindingId/v1":"cd0a3de973bf96a0fa099e3486a2a9266ba2a74f499eb056926a494b6fdf2316"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8\u201310 lines \u00D7 2): crates/koharu-diffusion/src/image.rs:23-32 | crates/koharu-diffusion/src/image.rs:35-42 \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":"crates/koharu-diffusion/src/image.rs"},"region":{"startLine":23}}}],"partialFingerprints":{"codehealthFindingId/v1":"0afab54b15ac93c0628a1f4c459815b4928834c19b9adf401f71d9af8f81f3ea"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): crates/koharu-ml/src/bin/comic_onomatopoeia.rs:186-195 | crates/koharu-ml/src/bin/koharu_layout_rfdetr_seg_2xl.rs:172-181 \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":"crates/koharu-ml/src/bin/comic_onomatopoeia.rs"},"region":{"startLine":186}}}],"partialFingerprints":{"codehealthFindingId/v1":"64c7fb36f10a40a015fa35387c6d6b0e9d77f63856c7a08ac7a63913cebb50e1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (3\u201310 lines \u00D7 3): crates/koharu-ml/src/bin/koharu_layout_rfdetr_seg_2xl.rs:145-147 | crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs:93-102 | crates/koharu-ml/src/bin/speech_bubble_yolov8m.rs:92-101 \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":"crates/koharu-ml/src/bin/koharu_layout_rfdetr_seg_2xl.rs"},"region":{"startLine":145}}}],"partialFingerprints":{"codehealthFindingId/v1":"593939fd9730b4c3ab72c055c4eb9de6034a286a5cb433b1b3af818f560d7da4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (3\u201310 lines \u00D7 3): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:394-403 | crates/koharu-ml/src/comic_layout_yolo26s/model.rs:508-510 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:397-406 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":394}}}],"partialFingerprints":{"codehealthFindingId/v1":"52126dda85e757e4b91281afcca6354f470fdb910d859b24b71ea6e3631aa6bc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:614-623 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:565-574 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":614}}}],"partialFingerprints":{"codehealthFindingId/v1":"09fd7661448d0afe48ddb0cc81a42823cc6f82bf96736815aafa6b14d447e678"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1608-1617 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2119-2128 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1608}}}],"partialFingerprints":{"codehealthFindingId/v1":"65c99b9ae47535be0daa2dbebe885efa4969e59e98bda53b0584b093a9e7192f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9\u201310 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/processor.rs:140-148 | crates/koharu-ml/src/pp_doclayout_v3/processor.rs:164-173 \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":"crates/koharu-ml/src/comic_text_bubble_detector/processor.rs"},"region":{"startLine":140}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d35a2d1d1838907d0e73e1a76b42ec8a87afed0b204c7c35cb896090527efca"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20139 lines \u00D7 3): crates/koharu-app/src/commands/canvas.rs:161-167 | crates/koharu-app/src/commands/canvas.rs:193-199 | crates/koharu-app/src/commands/canvas.rs:299-307 \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":"crates/koharu-app/src/commands/canvas.rs"},"region":{"startLine":161}}}],"partialFingerprints":{"codehealthFindingId/v1":"ac0298f283b47ea3ff65e44074a81f5fd582e84e86aa0258d140e8a81bc171db"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:387-395 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:447-455 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:450-458 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":387}}}],"partialFingerprints":{"codehealthFindingId/v1":"6b0639eca9fcee311445f00eba2b96ce71a14fc1501f4d64c54eb25eaf4ada03"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:398-406 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:458-466 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:461-469 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":398}}}],"partialFingerprints":{"codehealthFindingId/v1":"7d19219c0c092ad2b3f73b517d629725095303b776e0d36020e6f156ef0c50ac"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:420-428 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:364-372 | crates/koharu-renderer/src/bubble.rs:256-264 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":420}}}],"partialFingerprints":{"codehealthFindingId/v1":"7d98fecf4867b54141d7bfa0b1b0678edc30d8b0e655ee4ffe07ede9d922711d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 3): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1389-1397 | crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs:1165-1173 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1607-1615 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1389}}}],"partialFingerprints":{"codehealthFindingId/v1":"6057eb81913c02bee31d9b4641a7baea40c7b72177820a81de45cc2c66a356cb"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8\u20139 lines \u00D7 2): crates/koharu-diffusion/src/image.rs:57-64 | crates/koharu-diffusion/src/image.rs:73-81 \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":"crates/koharu-diffusion/src/image.rs"},"region":{"startLine":57}}}],"partialFingerprints":{"codehealthFindingId/v1":"cff2ef961b8c3cec34cc90c5f9c42288cd5175cfa47e74bb2e5f26c9d101e259"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/bin/comic_layout_yolo26s.rs:162-170 | crates/koharu-ml/src/bin/speech_bubble_yolo11n.rs:73-81 \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":"crates/koharu-ml/src/bin/comic_layout_yolo26s.rs"},"region":{"startLine":162}}}],"partialFingerprints":{"codehealthFindingId/v1":"ff90796046dc4cbd13033fcd73596d9bc0c1ff383e79ba542788ffa788a40816"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:174-182 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:254-262 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":174}}}],"partialFingerprints":{"codehealthFindingId/v1":"b89396dcd308fa096b39c3b259f86dc38d7bca338f815e503ce685fc18efdcba"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:203-211 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:283-291 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":203}}}],"partialFingerprints":{"codehealthFindingId/v1":"fa1992402cf41e1f7d1b9e4b56e5b12e989d663962752ad37c77ff287bb119b9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:158-166 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:184-192 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":158}}}],"partialFingerprints":{"codehealthFindingId/v1":"c539324f425123f775a3356bd36562232ea72cc9c0ece6aaceb4e8152bb6e2d8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:346-354 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:394-402 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":346}}}],"partialFingerprints":{"codehealthFindingId/v1":"a54ec4ae7a801c0bd8bc3889369753a784e3d0948dd09b26533d9f1c91f34674"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:826-834 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:937-945 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":826}}}],"partialFingerprints":{"codehealthFindingId/v1":"2931fe69407df0e9f7f502d760395ac4b51360cd26c1a0492aca4ab2ad2c6051"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/paddle_ocr_vl/model.rs:969-977 | crates/koharu-ml/src/paddle_ocr_vl/model.rs:978-986 \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":"crates/koharu-ml/src/paddle_ocr_vl/model.rs"},"region":{"startLine":969}}}],"partialFingerprints":{"codehealthFindingId/v1":"14325086276a268e87cdbef583a5304b57b58b285fe863cff5192d861c3532ee"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-runtime/src/runtime/packages/diffusion.rs:159-167 | crates/koharu-runtime/src/runtime/packages/llama.rs:159-167 \u2014 before extracting anything, compare \u0060crates/koharu-runtime/src/runtime/packages/diffusion.rs\u0060 and \u0060crates/koharu-runtime/src/runtime/packages/llama.rs\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 56 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":"crates/koharu-runtime/src/runtime/packages/diffusion.rs"},"region":{"startLine":159}}}],"partialFingerprints":{"codehealthFindingId/v1":"bba8fbd6a640982098c502c78ba107b9a9ce1f36c18ed350ba21bddc32a03e56"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-torch/src/nn/conv.rs:124-132 | crates/koharu-torch/src/nn/conv_transpose.rs:63-71 \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":"crates/koharu-torch/src/nn/conv.rs"},"region":{"startLine":124}}}],"partialFingerprints":{"codehealthFindingId/v1":"9f1ec51888b8b67baa432561ba71c2c17ef275548b154da848f81b4f0c451b4a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-torch/src/tensor/npy.rs:187-195 | crates/koharu-torch/src/tensor/npy.rs:210-218 \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":"crates/koharu-torch/src/tensor/npy.rs"},"region":{"startLine":187}}}],"partialFingerprints":{"codehealthFindingId/v1":"b778f90eafde4b88ebc688705c180b7f1d13a21b9fa4d9a6900a0f6c485dead9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-torch/src/vision/resnet.rs:72-80 | crates/koharu-torch/src/vision/resnet.rs:165-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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-torch/src/vision/resnet.rs"},"region":{"startLine":72}}}],"partialFingerprints":{"codehealthFindingId/v1":"9e3973a8673eb8b91dd1a187d5fb1fc7a43c9f7ecf9f425a8313ff07f69fb89f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): crates/koharu-llama/src/context/session.rs:205-212 | crates/koharu-llama/src/context/session.rs:303-310 | crates/koharu-llama/src/context/session.rs:412-419 \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":"crates/koharu-llama/src/context/session.rs"},"region":{"startLine":205}}}],"partialFingerprints":{"codehealthFindingId/v1":"ba50996202043c8565d40d8e9d9b0dd875323b9fd433850cd4786df64a9d9d0e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): crates/koharu-ml/src/aot_inpainting/processor.rs:121-128 | crates/koharu-ml/src/rorem_mixed/processor.rs:85-92 | crates/koharu-ml/src/rorem_mixed/processor.rs:149-156 \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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":121}}}],"partialFingerprints":{"codehealthFindingId/v1":"8731baa1fd87ec1bb96b692a4501862b4dadc217c0550691649a8ff588d67446"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): crates/koharu-ml/src/manga_ocr/processor.rs:59-66 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:102-109 | crates/koharu-ml/src/pp_ocr_v6/rec/processor.rs:121-128 \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":"crates/koharu-ml/src/manga_ocr/processor.rs"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"67525445a6989a6e729c620bd9d7be3b708f86f0dfc9a34b86d6133fdb84b0e1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-llama/src/context/kv_cache.rs:60-67 | crates/koharu-llama/src/context/kv_cache.rs:146-153 \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":"crates/koharu-llama/src/context/kv_cache.rs"},"region":{"startLine":60}}}],"partialFingerprints":{"codehealthFindingId/v1":"de280d51b0509177f648c8b954f99c1911c582d8e61df1acab543bcdb32fe6ed"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/aot_inpainting/processor.rs:102-109 | crates/koharu-ml/src/rorem_mixed/processor.rs:139-146 \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":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":102}}}],"partialFingerprints":{"codehealthFindingId/v1":"dcce6772d3216b257662d133b1fd93b245012380d572a6eb0d335796f8224657"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/aot_inpainting/processor.rs:136-143 | crates/koharu-ml/src/rorem_mixed/processor.rs:164-171 \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":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":136}}}],"partialFingerprints":{"codehealthFindingId/v1":"fb273abd175bba82f67003a234c1c88fd732164336e2b313609cd40d7c631a2d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:320-327 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:264-271 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":320}}}],"partialFingerprints":{"codehealthFindingId/v1":"2a20043cb2fffa004f229a2d4c6a5926c519e4906be7fcc3de9e3c952247eb7f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:653-660 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:604-611 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":653}}}],"partialFingerprints":{"codehealthFindingId/v1":"002729db297e548a7a7bca6fa5dd39890a65dd63e4a4ab36a825a7ea293d2e09"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1738-1745 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2270-2277 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1738}}}],"partialFingerprints":{"codehealthFindingId/v1":"13371448b6a02006f730efe57fbad4f5668e76bf95ef75fd8a7218d9c82c1e76"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/comic_text_detector/processor.rs:1227-1234 | crates/koharu-ml/src/comic_text_detector/processor.rs:1248-1255 \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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":1227}}}],"partialFingerprints":{"codehealthFindingId/v1":"3a94489137dcd86536d6340adaddc567a61d3e3ed1529ff07d89875a2abaf023"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/manga_ocr/model.rs:551-558 | crates/koharu-ml/src/manga_ocr/model.rs:916-923 \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":"crates/koharu-ml/src/manga_ocr/model.rs"},"region":{"startLine":551}}}],"partialFingerprints":{"codehealthFindingId/v1":"be643f4d0ecfab353c8f7b3f64e01c1964178e41d54b82ae74e48841aa5f7f13"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20138 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:155-162 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:147-153 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 10 separate duplicated blocks between them, totalling at least 131 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":155}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d9413d502116e8d8a2cb4a95e74467ebf53cec6ad5fa3e7a32a8a121d793e21"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-pipeline/src/stages/detection.rs:979-986 | crates/koharu-pipeline/src/stages/detection.rs:990-997 \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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":979}}}],"partialFingerprints":{"codehealthFindingId/v1":"6d9be6a0c6517a3e62ba705907359ad3aae5089da589c811381e9f19a7b34e5c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-torch/src/wrappers/tensor.rs:587-594 | crates/koharu-torch/src/wrappers/tensor.rs:613-620 \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":"crates/koharu-torch/src/wrappers/tensor.rs"},"region":{"startLine":587}}}],"partialFingerprints":{"codehealthFindingId/v1":"e9ac47631b2322bd3d3f5dfde538e259db2a7fddf8dd7c01efae4856bb2a8beb"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7\u20138 lines \u00D7 2): crates/koharu-torch/src/vision/resnet.rs:32-38 | crates/koharu-torch/src/vision/resnet.rs:116-123 \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":"crates/koharu-torch/src/vision/resnet.rs"},"region":{"startLine":32}}}],"partialFingerprints":{"codehealthFindingId/v1":"2e26039bd7feb486bc2badab42cbee41c0bafc2acf36562afb4b9ca3abe2c15b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 5): crates/koharu-llama/src/context/kv_cache.rs:58-64 | crates/koharu-llama/src/context/kv_cache.rs:92-98 | crates/koharu-llama/src/context/kv_cache.rs:144-150 | crates/koharu-llama/src/context/kv_cache.rs:180-186 | crates/koharu-llama/src/context/kv_cache.rs:214-220 \u2014 all 5 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":"crates/koharu-llama/src/context/kv_cache.rs"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"60ee3dd6afca3c17b18c8693b59474d3f30b340e653574412da1df925645f0c3"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 3): crates/koharu-app/src/commands/editing.rs:53-59 | crates/koharu-app/src/commands/editing.rs:134-140 | crates/koharu-app/src/commands/editing.rs:165-171 \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":"crates/koharu-app/src/commands/editing.rs"},"region":{"startLine":53}}}],"partialFingerprints":{"codehealthFindingId/v1":"b58592f8901e8d6ac2c23bcd1ef76ec717660f618b48488b72db92b9e5b678e7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6\u20137 lines \u00D7 3): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:411-417 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:355-361 | crates/koharu-renderer/src/bubble.rs:248-253 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":411}}}],"partialFingerprints":{"codehealthFindingId/v1":"b53cfdd4d1e56b2b6f0a1f8b232591ccfb32cb01a01e83e3a26adfa6c4c6a072"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:152-158 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:152-158 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":152}}}],"partialFingerprints":{"codehealthFindingId/v1":"d31b294a30267e9889093958d8dcf1c9642351d87f32392dc7721c9bcfcc5ac9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:369-375 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:337-343 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":369}}}],"partialFingerprints":{"codehealthFindingId/v1":"002bbacd5a35986b205dc86a0f9c7771f909df9d7fcec77715d69aec2315dcd2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:578-584 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:518-524 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":578}}}],"partialFingerprints":{"codehealthFindingId/v1":"d9d872c5aea9be551b970be576b72bac2a97009a6df3367ba57ee5dffd141f5f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (3\u20137 lines \u00D7 4): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:147-149 | crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:151-155 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:171-173 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:175-181 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":147}}}],"partialFingerprints":{"codehealthFindingId/v1":"be7733ceb2bceefeb820b84417abcdd9041defa6fba47da7e300f2f42d9060a9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs:247-253 | crates/koharu-ml/src/comic_text_bubble_detector/model.rs:402-408 \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":"crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs"},"region":{"startLine":247}}}],"partialFingerprints":{"codehealthFindingId/v1":"ae3bfeacb8193105ca8635435cb9c959a11cb3764480f61f568225c083414b35"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:662-668 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:613-619 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":662}}}],"partialFingerprints":{"codehealthFindingId/v1":"416ed2f0629d220e9620880681f823289df5e6ef30db276d464887913fcb5bfe"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1628-1634 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2132-2138 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1628}}}],"partialFingerprints":{"codehealthFindingId/v1":"4f61d9251c4d8e49b8dda0faac4f3ab4fff0079ef2cb365e840c7218bede906a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6\u20137 lines \u00D7 2): crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs:599-605 | crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs:609-614 \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":"crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs"},"region":{"startLine":599}}}],"partialFingerprints":{"codehealthFindingId/v1":"3a3ab4585d1b584c7645dc1d839df05f23b86f4811b2190971e1ca080514fe61"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/mod.rs:74-80 | crates/koharu-ml/src/speech_bubble_yolov8m/mod.rs:74-80 \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":"crates/koharu-ml/src/speech_bubble_yolo11n/mod.rs"},"region":{"startLine":74}}}],"partialFingerprints":{"codehealthFindingId/v1":"fa39a6ec37244edcc2c732ef77ec742d5657ec34102109a463b04164af06cfb7"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-rasterizer/src/native.rs:276-282 | crates/koharu-rasterizer/src/native.rs:335-341 \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":"crates/koharu-rasterizer/src/native.rs"},"region":{"startLine":276}}}],"partialFingerprints":{"codehealthFindingId/v1":"53184781a1b2c917a950fa9973b00d7b76889121196128f2dc5deab57453e899"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-torch/src/nn/var_store.rs:215-221 | crates/koharu-torch/src/nn/var_store.rs:276-282 \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":"crates/koharu-torch/src/nn/var_store.rs"},"region":{"startLine":215}}}],"partialFingerprints":{"codehealthFindingId/v1":"367a7d46d4c4e6cd39cc954ea564c902cbc43217ff99105e0044f371fb38dfab"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6\u20137 lines \u00D7 2): crates/koharu-torch/src/tensor/display.rs:220-225 | crates/koharu-torch/src/tensor/display.rs:228-234 \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":"crates/koharu-torch/src/tensor/display.rs"},"region":{"startLine":220}}}],"partialFingerprints":{"codehealthFindingId/v1":"989fb7865038c71dd119b3ae6cfceecc8caec8d7c75648e4097c6e049faa7b9f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 3): crates/koharu-torch/src/vision/inception.rs:53-58 | crates/koharu-torch/src/vision/inception.rs:97-102 | crates/koharu-torch/src/vision/inception.rs:148-154 \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":"crates/koharu-torch/src/vision/inception.rs"},"region":{"startLine":53}}}],"partialFingerprints":{"codehealthFindingId/v1":"4d1c5f3bf4dbf5463b35bf3262f74c54edb6b89b77e45866972389b42d33d4b1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-app/src/commands/canvas.rs:154-159 | crates/koharu-app/src/commands/canvas.rs:186-191 \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":"crates/koharu-app/src/commands/canvas.rs"},"region":{"startLine":154}}}],"partialFingerprints":{"codehealthFindingId/v1":"f93f36c9b89a872f7d4ffb4678713dd6135da3f732b59b1a271cb916302a3d4d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-diffusion/src/params.rs:799-804 | crates/koharu-diffusion/src/params.rs:1007-1012 \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":"crates/koharu-diffusion/src/params.rs"},"region":{"startLine":799}}}],"partialFingerprints":{"codehealthFindingId/v1":"19a64a01b033469d94d1b3778f644d7e53b89dd9b0ec8612a932d1136e1a161d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/aot_inpainting/processor.rs:187-192 | crates/koharu-ml/src/lama/processor.rs:287-292 \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":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":187}}}],"partialFingerprints":{"codehealthFindingId/v1":"8880d382ef1569e6e8fb5d491057ccc56406baeaeeb3c73c6e2dabcb2f51df01"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/baberu_ocr/model.rs:897-902 | crates/koharu-ml/src/hayai_ocr/model.rs:804-809 \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":"crates/koharu-ml/src/baberu_ocr/model.rs"},"region":{"startLine":897}}}],"partialFingerprints":{"codehealthFindingId/v1":"47660791756dad395eeb6ef56177aceb47918e8adbdbf2bcb7c8a8b74cc735da"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/bin/aot_inpainting.rs:27-32 | crates/koharu-ml/src/bin/lama.rs:24-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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/bin/aot_inpainting.rs"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"76938293388b86ef42ac1cdac2428b766dab3d1a15c56f103a6ae99eff087602"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:117-122 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:117-122 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":117}}}],"partialFingerprints":{"codehealthFindingId/v1":"26acc0afac1bbea4f9c3655c848111fc1e5152b77cd2cff362d87c6a1abceba6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:360-365 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:328-333 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":360}}}],"partialFingerprints":{"codehealthFindingId/v1":"b16e0781770849aaa30af4861d990fcd4b8a384904e26e78c04b89c43a5dbede"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:122-127 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:146-151 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":122}}}],"partialFingerprints":{"codehealthFindingId/v1":"b9459f767b48f7d9531f99c733f546ea4b05580475dbdcfdc2400429627e0804"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/comic_text_detector/model.rs:701-706 | crates/koharu-ml/src/comic_text_detector/model.rs:831-836 \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":"crates/koharu-ml/src/comic_text_detector/model.rs"},"region":{"startLine":701}}}],"partialFingerprints":{"codehealthFindingId/v1":"ed42490f3ab631dd9bb5713d0e4484cbb9d101df2c52c19d14d363e609387aaa"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/flux2_klein/mod.rs:81-86 | crates/koharu-ml/src/flux2_klein/mod.rs:233-238 \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":"crates/koharu-ml/src/flux2_klein/mod.rs"},"region":{"startLine":81}}}],"partialFingerprints":{"codehealthFindingId/v1":"c0c7abeb3dc62ebd6fc16ae59920341302f695f3df92d2c518af19c75cbad9ee"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:399-404 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:399-406 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 10 separate duplicated blocks between them, totalling at least 131 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":399}}}],"partialFingerprints":{"codehealthFindingId/v1":"73023a36b7dc013f5437555a4085eb401c81c90947d6ec684a5ed948e957347c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:422-427 | crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs:424-429 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 10 separate duplicated blocks between them, totalling at least 131 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs"},"region":{"startLine":422}}}],"partialFingerprints":{"codehealthFindingId/v1":"656439b10941b334e67f8d132e7923f07770a1d3f9eba83a0b4452bc7cc349a4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-pipeline/src/stages/inpainting.rs:817-823 | crates/koharu-renderer/src/bubble.rs:502-507 \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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":817}}}],"partialFingerprints":{"codehealthFindingId/v1":"310ae198978d7bfabb87eacab8b1d4715861d40e77c67b23e56e25186d6a914f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-rasterizer/src/frame.rs:34-39 | crates/koharu-renderer/src/frame.rs:41-46 \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":"crates/koharu-rasterizer/src/frame.rs"},"region":{"startLine":34}}}],"partialFingerprints":{"codehealthFindingId/v1":"dbe1a3f2e5d5fc3e9eda2a4d4a52abaf83cab951acb11ae9702045c2ba186caa"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (4\u20136 lines \u00D7 3): crates/koharu-renderer/src/layout.rs:914-919 | crates/koharu-renderer/src/layout.rs:1150-1153 | crates/koharu-renderer/src/layout.rs:1221-1226 \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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":914}}}],"partialFingerprints":{"codehealthFindingId/v1":"5b2a1935c270316c910702f5c9c450d490dc520168b8c4811bb52fe6a74cc48c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-renderer/src/renderer.rs:727-732 | crates/koharu-renderer/src/renderer.rs:946-951 \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":"crates/koharu-renderer/src/renderer.rs"},"region":{"startLine":727}}}],"partialFingerprints":{"codehealthFindingId/v1":"faf3118b9e5e0aebb2c40672b44cad071d752f6b5bb4a9b88deb11ce297e6d35"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-torch/src/nn/batch_norm.rs:48-53 | crates/koharu-torch/src/nn/group_norm.rs:46-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":"crates/koharu-torch/src/nn/batch_norm.rs"},"region":{"startLine":48}}}],"partialFingerprints":{"codehealthFindingId/v1":"68b00d7180725c73a27c078855aed559a5cc86c97cb84d426e5629254bcaae0d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-torch/src/wrappers/jit.rs:686-691 | crates/koharu-torch/src/wrappers/jit.rs:699-704 \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":"crates/koharu-torch/src/wrappers/jit.rs"},"region":{"startLine":686}}}],"partialFingerprints":{"codehealthFindingId/v1":"3e46e0c483a2e563de2c79c76aa70df19e174bb2871c2cdf50a40aa1bc0778da"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:402-406 | crates/koharu-ml/src/comic_text_detector/processor.rs:502-507 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:346-350 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/comic_text_detector/processor.rs\u0060 as WHOLE FILES: this scan already matched 3 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":402}}}],"partialFingerprints":{"codehealthFindingId/v1":"0df2fd5633b3ecb9216f1b8254faa0d69096923c8c4466e74aff9438b4da4d62"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): crates/koharu-ml/src/flux2_klein/mod.rs:81-85 | crates/koharu-ml/src/flux2_klein/mod.rs:180-184 | crates/koharu-ml/src/flux2_klein/mod.rs:233-238 \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":"crates/koharu-ml/src/flux2_klein/mod.rs"},"region":{"startLine":81}}}],"partialFingerprints":{"codehealthFindingId/v1":"2b27e35c444fe6cef7dced53c3792fbb475477d4ed56b97ac3838f4ef8ac7080"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): crates/koharu-torch/src/wrappers/jit.rs:543-547 | crates/koharu-torch/src/wrappers/jit.rs:566-570 | crates/koharu-torch/src/wrappers/jit.rs:852-856 \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":"crates/koharu-torch/src/wrappers/jit.rs"},"region":{"startLine":543}}}],"partialFingerprints":{"codehealthFindingId/v1":"03a5f4be50b43bdb8ebc6ce187413930951a1fb002a8a568f0524dc88fd2965b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-app/src/commands/agent/host.rs:37-41 | crates/koharu-app/src/commands/agent/host.rs:248-252 \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":"crates/koharu-app/src/commands/agent/host.rs"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"4ecbb9d7c6dc94025ff4eb750a0b186c3dca6ecc6ff9259cac617bbcc1087837"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-app/src/commands/canvas.rs:426-430 | crates/koharu-app/src/commands/project.rs:1274-1278 \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":"crates/koharu-app/src/commands/canvas.rs"},"region":{"startLine":426}}}],"partialFingerprints":{"codehealthFindingId/v1":"a8050a1252ba9bdf99d44c7c324fc158e612af691dad18d916b6f1eb0d1f95e5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:145-149 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:145-149 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":145}}}],"partialFingerprints":{"codehealthFindingId/v1":"4cf7b79a01c7cab842e547f4c1d6b92097f3360669730cdaf9b575e95b757ec2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:314-318 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:207-211 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":314}}}],"partialFingerprints":{"codehealthFindingId/v1":"2c54ee1f54249296efde78d85a12196a25ebf546f43554bfcbe96117000f6d47"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:321-326 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:373-377 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":321}}}],"partialFingerprints":{"codehealthFindingId/v1":"77f08d60cf9deda0e979f174bbd56c57059197a79aaed1295b52754289a3952a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs:609-613 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:560-564 \u2014 before extracting anything, compare \u0060crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs\u0060 and \u0060crates/koharu-ml/src/pp_ocr_v6/det/processor.rs\u0060 as WHOLE FILES: this scan already matched 18 separate duplicated blocks between them, totalling at least 281 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":"crates/koharu-ml/src/comic_onomatopoeia/detector/processor.rs"},"region":{"startLine":609}}}],"partialFingerprints":{"codehealthFindingId/v1":"349afad3fcef789b24c69803ee1c1e4e1981c96955dfba41780388b218ac7161"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs:205-209 | crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs:468-472 \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":"crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs"},"region":{"startLine":205}}}],"partialFingerprints":{"codehealthFindingId/v1":"8a4f9b0ac054396747153262e736d5fb7314275d6b923fd62fafd328d710154c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:992-996 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1116-1120 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":992}}}],"partialFingerprints":{"codehealthFindingId/v1":"833a4f0ec824598b8c6c3cf57e0c82424adf8a32ca7722b3d4c896f250a9a8ec"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/processor.rs:67-71 | crates/koharu-ml/src/manga_text_mask/processor.rs: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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/processor.rs"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"028f3f3fdd0b819e7ca904093c787ef23b97d8f7701321bba5cee078287d4373"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/pp_doclayout_v3/model.rs:2229-2233 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2235-2239 \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":"crates/koharu-ml/src/pp_doclayout_v3/model.rs"},"region":{"startLine":2229}}}],"partialFingerprints":{"codehealthFindingId/v1":"68ec312019688d0a663d0465a9500a67a405522af806c8062267ab1e4fc4c3a8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (3\u20135 lines \u00D7 3): crates/koharu-ml/src/pp_doclayout_v3/processor.rs:69-71 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:72-76 | crates/koharu-ml/src/pp_ocr_v6/rec/processor.rs:82-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":"crates/koharu-ml/src/pp_doclayout_v3/processor.rs"},"region":{"startLine":69}}}],"partialFingerprints":{"codehealthFindingId/v1":"055ab8870d48211342aead985b7da25071c20920a25443857960a035272dd04d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-pipeline/src/resources/vram.rs:159-163 | crates/koharu-pipeline/src/resources/windows.rs:106-110 \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":"crates/koharu-pipeline/src/resources/vram.rs"},"region":{"startLine":159}}}],"partialFingerprints":{"codehealthFindingId/v1":"18bc9e5ac660fde2bd952a1326467a79e851d039992a6c4fb737a4f2888bf8cd"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-pipeline/src/stages/detection.rs:1430-1434 | crates/koharu-pipeline/src/stages/inpainting.rs:497-501 \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":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":1430}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c8d4a48578f8edc4891bf6d43b0e0d0d761cc84e1e8e2a423ee43b358b29526"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-torch/src/vision/rust_image.rs:96-100 | crates/koharu-torch/src/vision/rust_image.rs:108-112 \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":"crates/koharu-torch/src/vision/rust_image.rs"},"region":{"startLine":96}}}],"partialFingerprints":{"codehealthFindingId/v1":"c4555007084cd93fe6e50fd9f6105fd3a0cabd43806344625820d405e86bf9cb"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-torch/src/vision/resnet.rs:67-71 | crates/koharu-torch/src/vision/resnet.rs:160-164 \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":"crates/koharu-torch/src/vision/resnet.rs"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"6e4a9ad77d09672c68196d7e62b7d1a62ab32357a4f297a2d1c7faff74006c5b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 3): crates/koharu-canvas/src/web.rs:78-84 | crates/koharu-desktop/src/lib.rs:24-30 | crates/koharu-rasterizer/src/prepared.rs:495-501 \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":"crates/koharu-canvas/src/web.rs"},"region":{"startLine":78}}}],"partialFingerprints":{"codehealthFindingId/v1":"aed659ddfdc9823bb3e48839546f7057ae3f830147ff60f5137cef4e889bf6dc"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 16): crates/koharu-ml/src/aot_inpainting/model.rs:47-51 | crates/koharu-ml/src/comic_layout_yolo26s/model.rs:40-44 | crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs:38-42 | crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs:81-85 | crates/koharu-ml/src/comic_text_bubble_detector/model.rs:38-42 | crates/koharu-ml/src/font_detector/model.rs:39-43 | crates/koharu-ml/src/hayai_ocr/model.rs:71-75 | crates/koharu-ml/src/lama/model.rs:31-35 | crates/koharu-ml/src/manga_ocr/model.rs:65-69 | crates/koharu-ml/src/manga_text_mask/model.rs:44-48 | crates/koharu-ml/src/paddle_ocr_vl/model.rs:59-63 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:39-43 | crates/koharu-ml/src/pp_ocr_v6/det/model.rs:44-48 | crates/koharu-ml/src/pp_ocr_v6/rec/model.rs:37-41 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:40-44 | crates/koharu-ml/src/speech_bubble_yolov8m/model.rs:37-41 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/aot_inpainting/model.rs"},"region":{"startLine":47}}}],"partialFingerprints":{"codehealthFindingId/v1":"083ef6cfed2f177ed9e2adc56a58680fd4af3bcfc5f7e93849c9ac1287973f01"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/aot_inpainting/processor.rs:173-180 | crates/koharu-ml/src/lama/processor.rs:330-337 \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":"crates/koharu-ml/src/aot_inpainting/processor.rs"},"region":{"startLine":173}}}],"partialFingerprints":{"codehealthFindingId/v1":"2230b16ea6681138629521140bf475d8862a1442137622bc216355f9c6ebcf34"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/baberu_ocr/model.rs:875-879 | crates/koharu-ml/src/paddle_ocr_vl/model.rs:887-891 \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":"crates/koharu-ml/src/baberu_ocr/model.rs"},"region":{"startLine":875}}}],"partialFingerprints":{"codehealthFindingId/v1":"edeb2a37277bce75792ff0f5faf8d54bcf03bfeb2d897643352a2b6e80e8511e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/baberu_ocr/model.rs:889-894 | crates/koharu-ml/src/paddle_ocr_vl/model.rs:901-906 \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":"crates/koharu-ml/src/baberu_ocr/model.rs"},"region":{"startLine":889}}}],"partialFingerprints":{"codehealthFindingId/v1":"a50b0a4353780960e62ffe74518c3cb56bade62d3a39b7e720ae5bc05808c547"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/bin/comic_onomatopoeia.rs:124-128 | crates/koharu-ml/src/bin/koharu_layout_rfdetr_seg_2xl.rs:92-96 \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":"crates/koharu-ml/src/bin/comic_onomatopoeia.rs"},"region":{"startLine":124}}}],"partialFingerprints":{"codehealthFindingId/v1":"43db243ca81fc985e81631fbdf6cffa706525d4265a3822425fe1bb75c5edad0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/bin/comic_onomatopoeia.rs:221-229 | crates/koharu-ml/src/bin/pp_ocr_v6.rs:84-92 \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":"crates/koharu-ml/src/bin/comic_onomatopoeia.rs"},"region":{"startLine":221}}}],"partialFingerprints":{"codehealthFindingId/v1":"99289d5f286eb0ed02803dbc2da6927ef2b46cc20f9b0f2a5bfbd424555bf9e5"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 4): crates/koharu-ml/src/comic_layout_yolo26s/config.rs:20-24 | crates/koharu-ml/src/pp_doclayout_v3/processor.rs:59-63 | crates/koharu-ml/src/speech_bubble_yolo11n/config.rs:23-27 | crates/koharu-ml/src/speech_bubble_yolov8m/config.rs:23-27 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 4 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 4 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/config.rs"},"region":{"startLine":20}}}],"partialFingerprints":{"codehealthFindingId/v1":"d598032b589189f401ca61848ce0b2392cc0d1732bad7b6260bf7d21a8b8b9d8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:230-234 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:310-314 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":230}}}],"partialFingerprints":{"codehealthFindingId/v1":"5699949f1753d4081413c686c03697c125d8b4b306ff43f585289f305591fb39"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:418-423 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:421-426 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":418}}}],"partialFingerprints":{"codehealthFindingId/v1":"f39738edcf199b6e96b8d2852cd303a3154501ff93837f1b1611ea73981489a9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:490-496 | crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:493-499 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 21 separate duplicated blocks between them, totalling at least 202 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":490}}}],"partialFingerprints":{"codehealthFindingId/v1":"11f92ddeae2bb31db3723de3b3343df677d294ddd2916a08aab49784a1055d9b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs:151-155 | crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs:511-515 \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":"crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs"},"region":{"startLine":151}}}],"partialFingerprints":{"codehealthFindingId/v1":"f5c47e9852464eb8a8cc6ac9a7af673e524190c2a1324d4e5a5580f031825289"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 4): crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs:383-396 | crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs:759-772 | crates/koharu-ml/src/comic_text_detector/model.rs:852-865 | crates/koharu-ml/src/manga_text_mask/model.rs:576-589 \u2014 the copies span different directories, so extracting a shared function means choosing where it lives: put it somewhere all 4 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 4 times."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_onomatopoeia/detector/model.rs"},"region":{"startLine":383}}}],"partialFingerprints":{"codehealthFindingId/v1":"94ab0ce1fdbb9ab78004aa4fa77607d4622a675a9774eb7b01623eefe5887610"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/recognizer/mod.rs:53-59 | crates/koharu-ml/src/pp_ocr_v6/rec/mod.rs:70-76 \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":"crates/koharu-ml/src/comic_onomatopoeia/recognizer/mod.rs"},"region":{"startLine":53}}}],"partialFingerprints":{"codehealthFindingId/v1":"0c5056ddfcccebbc0abbd9bb23b98cb85fd4b2bfbfcdbf43050fb4e9ee447ea1"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2): crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs:782-795 | crates/koharu-ml/src/pp_ocr_v6/det/model.rs:73-86 \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":"crates/koharu-ml/src/comic_onomatopoeia/recognizer/model.rs"},"region":{"startLine":782}}}],"partialFingerprints":{"codehealthFindingId/v1":"ed6235e9149bf018d4e2885a0d599fc08818bb2a21dbc89ba891833c4ff7b76b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1332-1348 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:1550-1566 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1332}}}],"partialFingerprints":{"codehealthFindingId/v1":"a61b342269e3c01183123a73f371eb387a32587a580554a1be16c95d330fd17e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1570-1577 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2083-2090 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1570}}}],"partialFingerprints":{"codehealthFindingId/v1":"d5d5c8f5a520a7331caf385306cd7ee046a1d9e578391baa77099ab4a36d9172"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1579-1586 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2092-2099 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1579}}}],"partialFingerprints":{"codehealthFindingId/v1":"ce4d722b86afe7ff6b95702588945bb187a8d83673cec9c0a2a9b9c35798aa6e"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1730-1735 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2262-2267 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1730}}}],"partialFingerprints":{"codehealthFindingId/v1":"717042ef15be276055d14ca35c0f364b523191ee7ce12344590ccf86e504a5a4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3): crates/koharu-ml/src/comic_text_bubble_detector/processor.rs:518-525 | crates/koharu-pipeline/src/stages/detection.rs:1590-1597 | crates/koharu-pipeline/src/stages/inpainting.rs:713-720 \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":"crates/koharu-ml/src/comic_text_bubble_detector/processor.rs"},"region":{"startLine":518}}}],"partialFingerprints":{"codehealthFindingId/v1":"0be3ecf1b5bd0e4f995817127514ca55df4a538115bda95056a58bc90ddd3047"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/pp_doclayout_v3/model.rs:813-818 | crates/koharu-ml/src/pp_ocr_v6/pp_lcnet_v4.rs:275-280 \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":"crates/koharu-ml/src/pp_doclayout_v3/model.rs"},"region":{"startLine":813}}}],"partialFingerprints":{"codehealthFindingId/v1":"faa7a3daa01cb5af035947e0a6cd2da36491fe10798e0673b012ae9aca232208"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:60-64 | crates/koharu-ml/src/pp_ocr_v6/rec/processor.rs:70-74 \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":"crates/koharu-ml/src/pp_ocr_v6/det/processor.rs"},"region":{"startLine":60}}}],"partialFingerprints":{"codehealthFindingId/v1":"00397a0c346784a9d9141ac6e07cc60b355a9c3618a18302031d5560ae3784b2"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:353-367 | crates/koharu-ml/src/speech_bubble_yolov8m/model.rs:280-294 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 4 separate duplicated blocks between them, totalling at least 32 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/model.rs"},"region":{"startLine":353}}}],"partialFingerprints":{"codehealthFindingId/v1":"6969e5381f3f0f7990845d0389e08dc938cf68d5cb96fb9d81ba36081cc77c1b"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:581-586 | crates/koharu-ml/src/speech_bubble_yolov8m/model.rs:648-653 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 4 separate duplicated blocks between them, totalling at least 32 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/model.rs"},"region":{"startLine":581}}}],"partialFingerprints":{"codehealthFindingId/v1":"14885c5aeb83113695f840a82d3679b02d9cd0fe49cd343f36054e1cc9e44a8a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-ml/src/speech_bubble_yolo11n/model.rs:588-593 | crates/koharu-ml/src/speech_bubble_yolov8m/model.rs:674-679 \u2014 \u0060crates/koharu-ml/src/speech_bubble_yolo11n/model.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolov8m/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 4 separate duplicated blocks between them, totalling at least 32 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/speech_bubble_yolo11n/model.rs"},"region":{"startLine":588}}}],"partialFingerprints":{"codehealthFindingId/v1":"92f105a44245cbd5019549e7a56b348fed99caf156bd0212029edab7bd00167d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-pipeline/src/resources/linux.rs:139-146 | crates/koharu-pipeline/src/resources/windows.rs:120-127 \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":"crates/koharu-pipeline/src/resources/linux.rs"},"region":{"startLine":139}}}],"partialFingerprints":{"codehealthFindingId/v1":"3ff18e362ba3f77e4ddd95e53b1023304fbada899bb5eec741244141cbfd0dcb"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): crates/koharu-pipeline/src/stages/detection.rs:120-124 | crates/koharu-pipeline/src/stages/inpainting.rs:127-131 | crates/koharu-pipeline/src/stages/ocr.rs:50-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 3 call sites, so a change lands once."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-pipeline/src/stages/detection.rs"},"region":{"startLine":120}}}],"partialFingerprints":{"codehealthFindingId/v1":"e14b30d202dc886e16348d5f413f83ce2f262dbbb90302ee22a0f1d317867197"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-rasterizer/src/frame.rs:77-82 | crates/koharu-renderer/src/frame.rs:81-86 \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":"crates/koharu-rasterizer/src/frame.rs"},"region":{"startLine":77}}}],"partialFingerprints":{"codehealthFindingId/v1":"976bdea35e44804e72d3de59312697ab20cd9ceaca9a79a4bb8959702ae4fb56"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-rasterizer/src/frame.rs:299-304 | crates/koharu-rasterizer/src/prepared.rs:596-601 \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":"crates/koharu-rasterizer/src/frame.rs"},"region":{"startLine":299}}}],"partialFingerprints":{"codehealthFindingId/v1":"1f1324c2f79badbbca5100a15e1afd5eace79c5f17eff52505e32cf481fbe64d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-torch/src/vision/densenet.rs:10-18 | crates/koharu-torch/src/vision/resnet.rs:7-15 \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":"crates/koharu-torch/src/vision/densenet.rs"},"region":{"startLine":10}}}],"partialFingerprints":{"codehealthFindingId/v1":"96d6862382bf2ee20d71c5e9a48aa929ec56ce42bad10b25ddbc1b1b63f1a74a"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 2): crates/koharu-translator/src/remote/lm_studio.rs:130-137 | crates/koharu-translator/src/remote/openai_compatible.rs:127-134 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai_compatible.rs\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 140 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":"crates/koharu-translator/src/remote/lm_studio.rs"},"region":{"startLine":130}}}],"partialFingerprints":{"codehealthFindingId/v1":"043248bead5904f57e1934b45d5a091ecd2d0e43027cb9c8a935e7a264914462"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 3): crates/koharu-ml/src/baberu_ocr/model.rs:315-321 | crates/koharu-ml/src/manga_ocr/model.rs:434-440 | crates/koharu-ml/src/manga_ocr/model.rs:710-716 \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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/baberu_ocr/model.rs"},"region":{"startLine":315}}}],"partialFingerprints":{"codehealthFindingId/v1":"fb4e5ab137b0ee48a4ec457a44cfce8a09ad23e7cee3d60ca631a7e37a8fa392"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-torch/src/wrappers/jit.rs:616-620 | crates/koharu-torch/src/wrappers/tensor.rs:565-569 \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":"crates/koharu-torch/src/wrappers/jit.rs"},"region":{"startLine":616}}}],"partialFingerprints":{"codehealthFindingId/v1":"f0a262dae336139aa3bbe8c59d910260bfbd3fc8c051044e1a7dd06c7d39c139"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (25 lines \u00D7 2): crates/koharu-canvas/src/surface.rs:22-46 | crates/koharu-rasterizer/src/compositor.rs:172-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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-canvas/src/surface.rs"},"region":{"startLine":22}}}],"partialFingerprints":{"codehealthFindingId/v1":"78235bd28b801c1082d10dff28e06a7e10654244b55898a932470934536a22ef"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-ml/src/manga_ocr/model.rs:464-475 | crates/koharu-ml/src/manga_ocr/model.rs:761-772 \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":"crates/koharu-ml/src/manga_ocr/model.rs"},"region":{"startLine":464}}}],"partialFingerprints":{"codehealthFindingId/v1":"f60cf0403c4f43204b7eec1ff4ef68f7308614b69d5e21db4a8958b9f0a06581"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): crates/koharu-translator/src/remote/deepseek.rs:26-36 | crates/koharu-translator/src/remote/minimax.rs:22-32 \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":"crates/koharu-translator/src/remote/deepseek.rs"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"d753ad465672eed00b173707f346bfe18621a54beba17c8105af31797adfab58"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/lama/model.rs:332-340 | crates/koharu-ml/src/lama/model.rs:341-350 \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":"crates/koharu-ml/src/lama/model.rs"},"region":{"startLine":332}}}],"partialFingerprints":{"codehealthFindingId/v1":"7652e9288b5b76d45ce29b311c10c923e7f91cc9842c8cd281270d7094d96ca9"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): crates/koharu-runtime/src/runtime/packages/diffusion.rs:101-110 | crates/koharu-runtime/src/runtime/packages/llama.rs:101-110 \u2014 before extracting anything, compare \u0060crates/koharu-runtime/src/runtime/packages/diffusion.rs\u0060 and \u0060crates/koharu-runtime/src/runtime/packages/llama.rs\u0060 as WHOLE FILES: this scan already matched 4 separate duplicated blocks between them, totalling at least 56 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":"crates/koharu-runtime/src/runtime/packages/diffusion.rs"},"region":{"startLine":101}}}],"partialFingerprints":{"codehealthFindingId/v1":"e85f5fc123c663ebad0d334bcd09b29d0365278b4f9d85aa498f521912d4f221"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2): crates/koharu-ml/src/aot_inpainting/mod.rs:27-36 | crates/koharu-ml/src/manga_text_mask/mod.rs:27-36 \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":"crates/koharu-ml/src/aot_inpainting/mod.rs"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"c74c980558c8bf6ea34fbeb86df36a58f2d11bdc1c054df7d67782d3d15cd7bd"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-translator/src/remote/lm_studio.rs:85-93 | crates/koharu-translator/src/remote/openai_compatible.rs:85-93 \u2014 before extracting anything, compare \u0060crates/koharu-translator/src/remote/lm_studio.rs\u0060 and \u0060crates/koharu-translator/src/remote/openai_compatible.rs\u0060 as WHOLE FILES: this scan already matched 7 separate duplicated blocks between them, totalling at least 140 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":"crates/koharu-translator/src/remote/lm_studio.rs"},"region":{"startLine":85}}}],"partialFingerprints":{"codehealthFindingId/v1":"550f672a830f71680abe0eeeb6825816a96fb614d811ccb2fe00794b40253b17"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/lama/processor.rs:157-165 | crates/koharu-ml/src/lama/processor.rs:169-177 \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":"crates/koharu-ml/src/lama/processor.rs"},"region":{"startLine":157}}}],"partialFingerprints":{"codehealthFindingId/v1":"371d2edeab63107dbee8a9ab793afd28080a6bb1961b658d81eedab0a26b1431"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 4): crates/koharu-pipeline/src/stages/inpainting.rs:191-199 | crates/koharu-pipeline/src/stages/inpainting.rs:216-224 | crates/koharu-pipeline/src/stages/inpainting.rs:238-246 | crates/koharu-pipeline/src/stages/inpainting.rs:266-274 \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."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":191}}}],"partialFingerprints":{"codehealthFindingId/v1":"a4c52fcaf940c39716490cd04484d4c651140fb12ac00335c655ea75a4af3924"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/pp_doclayout_v3/processor.rs:100-106 | crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:95-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":"crates/koharu-ml/src/pp_doclayout_v3/processor.rs"},"region":{"startLine":100}}}],"partialFingerprints":{"codehealthFindingId/v1":"8ceeeecb40ae0597e407728add1642277fd352f38aa3ceee7a8dff673245e1a4"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_text_detector/processor.rs:95-101 | crates/koharu-ml/src/comic_text_detector/processor.rs:304-310 \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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":95}}}],"partialFingerprints":{"codehealthFindingId/v1":"913209297adea86f87606c578d5954d43052f6d7aea8c05d5b1a98670b1bb2f6"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-agent/src/codex/catalog.rs:19-24 | crates/koharu-agent/src/codex/mod.rs:92-97 \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":"crates/koharu-agent/src/codex/catalog.rs"},"region":{"startLine":19}}}],"partialFingerprints":{"codehealthFindingId/v1":"b1d1044d2ae580eeea50616e565d8b0f95201ee107ab73495771c9286ab9f06d"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/pp_ocr_v6/det/processor.rs:67-71 | crates/koharu-ml/src/pp_ocr_v6/rec/processor.rs:77-81 \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":"crates/koharu-ml/src/pp_ocr_v6/det/processor.rs"},"region":{"startLine":67}}}],"partialFingerprints":{"codehealthFindingId/v1":"e4eb91721a0f597f8ef253ae537e1ac057c84796c00de5a4d566f11e5f2b1e0f"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2): crates/koharu-canvas/src/web.rs:1841-1854 | crates/koharu-desktop/src/lib.rs:512-525 \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":"crates/koharu-canvas/src/web.rs"},"region":{"startLine":1841}}}],"partialFingerprints":{"codehealthFindingId/v1":"0bc2b37e6ec0bfe5560680cedb35715a587bfc47ace29bb6f711b307869c7c6c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/model.rs:612-618 | crates/koharu-ml/src/comic_layout_yolo26s/model.rs:626-632 \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":"crates/koharu-ml/src/comic_layout_yolo26s/model.rs"},"region":{"startLine":612}}}],"partialFingerprints":{"codehealthFindingId/v1":"0596e6a13302f71f5eb6b8ff42affd0d5c3ed9b59fe7a733462bb3973857b7ad"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2): crates/koharu-ml/src/comic_layout_yolo26s/processor.rs:335-343 | crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs:383-391 \u2014 \u0060crates/koharu-ml/src/comic_layout_yolo26s/processor.rs\u0060 and \u0060crates/koharu-ml/src/speech_bubble_yolo11n/processor.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 13 separate duplicated blocks between them, totalling at least 160 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_layout_yolo26s/processor.rs"},"region":{"startLine":335}}}],"partialFingerprints":{"codehealthFindingId/v1":"f3dce63b8e8c77d01487779bacf93b406f8295f93c57054e9b82404fe4b0e9d8"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (12 lines \u00D7 2): crates/koharu-ml/src/comic_text_bubble_detector/model.rs:1547-1558 | crates/koharu-ml/src/pp_doclayout_v3/model.rs:2060-2071 \u2014 \u0060crates/koharu-ml/src/comic_text_bubble_detector/model.rs\u0060 and \u0060crates/koharu-ml/src/pp_doclayout_v3/model.rs\u0060 are one unit implemented once per sibling directory, so they are most likely parallel implementations of one contract rather than a copy of each other \u2014 this scan matched 25 separate duplicated blocks between them, totalling at least 345 lines. If both are selected at run time, neither can be retired in favour of the other, and the lines that DIFFER between them are the reason both exist. The move that pays here is to hoist the identical part into a shared location the whole family can reach and give what differs a parameter or a seam, so a change lands once instead of once per sibling; extracting one helper per block leaves every sibling to drift on its own."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_bubble_detector/model.rs"},"region":{"startLine":1547}}}],"partialFingerprints":{"codehealthFindingId/v1":"d8980eea5b9c7fa7514e534d814f44b302721f25d870c8c6b35f537371903131"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_text_detector/model.rs:762-766 | crates/koharu-ml/src/comic_text_detector/model.rs:792-796 \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":"crates/koharu-ml/src/comic_text_detector/model.rs"},"region":{"startLine":762}}}],"partialFingerprints":{"codehealthFindingId/v1":"e7e47ad689939882a51725c99434a519b27af996666f59d6eb51ecd460777db0"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): crates/koharu-ml/src/comic_text_detector/processor.rs:861-865 | crates/koharu-ml/src/comic_text_detector/processor.rs:868-872 \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":"crates/koharu-ml/src/comic_text_detector/processor.rs"},"region":{"startLine":861}}}],"partialFingerprints":{"codehealthFindingId/v1":"b4736663c7c079921e7500b34c5029c770a867ae325edc5939eda5b5b09f424c"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 3): crates/koharu-scene/src/edit.rs:427-431 | crates/koharu-scene/src/edit.rs:535-539 | crates/koharu-scene/src/edit.rs:750-754 \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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":427}}}],"partialFingerprints":{"codehealthFindingId/v1":"a4e34c5dff76ead1659efeffd939cbff7e8bbeafa63bdaf6040e25e74a42f542"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2): crates/koharu-scene/src/patch.rs:131-136 | crates/koharu-scene/src/patch.rs:156-161 \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":"crates/koharu-scene/src/patch.rs"},"region":{"startLine":131}}}],"partialFingerprints":{"codehealthFindingId/v1":"59acf3e6d589eb4001e7ecc0842651bc75d75e681ec1d77d7270d4f09eebf6d7"}},{"ruleId":"D5","level":"warning","message":{"text":"Unstable project koharu-app: koharu-app has instability 0.92 with 1 dependents."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"20c4983d5d1fea20caa03d83d4809094f4fb6e51c412379f346379df6daec111"}},{"ruleId":"D5","level":"warning","message":{"text":"Unstable project koharu-pipeline: koharu-pipeline has instability 0.86 with 1 dependents."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"4d6eb9f214458adc18d342da7fe18ed0279b782b7c9166c2849a7f6ffdac6cac"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: @koharu/ui: @koharu/ui: abstractness 0.00, instability 0.00, distance 1.00 \u2014 zone of pain \u2014 concrete and depended on by 1 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"62c82ab75485d3ab369f3be5fa245152657d9c29d3c4bc8bf1f16c66b643fecf"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-bindgen: koharu-bindgen: abstractness 0.00, instability 0.00, distance 1.00 \u2014 zone of pain \u2014 concrete and depended on by 3 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"70ead6898cc6d4ab6117e96612f2bac843dc3d97c4b843160c9c2d3dcb83fc7b"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-config: koharu-config: abstractness 0.00, instability 0.00, distance 1.00 \u2014 zone of pain \u2014 concrete and depended on by 5 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"ed787cc0229d3a0ca435cd7cef8e5339a447307639c03b22217abda9a93c4c80"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-rasterizer: koharu-rasterizer: abstractness 0.00, instability 0.00, distance 1.00 \u2014 zone of pain \u2014 concrete and depended on by 6 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"a0ea3308fc1b39edb5caf1ff7ff6bdbe89a09da0030ef1b5e102ae2e1f920e6a"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-secrets: koharu-secrets: abstractness 0.00, instability 0.00, distance 1.00 \u2014 zone of pain \u2014 concrete and depended on by 3 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"30ab2e70d0ceb0ce568da206a8989cf81538bdf1b7c780d9f680a13c574e3bd5"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-storage: koharu-storage: abstractness 0.00, instability 0.00, distance 1.00 \u2014 zone of pain \u2014 concrete and depended on by 1 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"9590d33fcd4ab3bd784e809d5182856057743a252b184edc994f04ab4e6e68c9"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-metrics: koharu-metrics: abstractness 0.11, instability 0.00, distance 0.89 \u2014 zone of pain \u2014 concrete and depended on by 2 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"c5ab8faa5fb0e8b1b416ab059ef68fe11e37be256768805d024069ac930afc58"}},{"ruleId":"D5","level":"warning","message":{"text":"Off the main sequence: koharu-scene: koharu-scene: abstractness 0.05, instability 0.17, distance 0.78 \u2014 zone of pain \u2014 concrete and depended on by 5 project(s), so it\u0027s rigid to change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"b51d96462578d4cac9ff0054b9e954e8064c9c4fa479673332278a2587b0f326"}},{"ruleId":"D8","level":"warning","message":{"text":"Coverage not measured \u2014 JavaScript/TypeScript suite: Coverage NOT MEASURED: the JavaScript/TypeScript half could not be measured \u2014 packages/koharu/ runs vitest but declares no coverage provider, so the suite can run and still produce no lcov \u2014 add \u0060@vitest/coverage-v8\u0060 (or \u0060@vitest/coverage-istanbul\u0060) as a devDependency. Coverage is excluded from the score rather than counted as a near-zero. The named suite step is one the repository\u0027s maintainers can perform; once it passes, the real number is measured on the next scan. Alternatively, commit the lcov/Cobertura report your CI produces and it is read without a re-run."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"62e63e619c28f057e129f2997c965d32a457366a9ce18ca019ffb6bdfba85c99"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/editor/CanvasWorkspace.tsx: packages/koharu/components/editor/CanvasWorkspace.tsx changed 11 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 141 in CanvasWorkspace.CanvasWorkspace at line 83. 2 of those changes were fix/bug commits, and the other 9 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/editor/CanvasWorkspace.tsx\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":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"34b43d6943ac5da87b8a4b3ee877195e4e49d1e9a0644cdbf2f6c1a593d2f097"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-renderer/src/layout.rs: crates/koharu-renderer/src/layout.rs changed 16 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 58 in TextLayout::run_with_size at line 463. 4 of those changes were fix/bug commits, and the other 12 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-renderer/src/layout.rs\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":"crates/koharu-renderer/src/layout.rs"},"region":{"startLine":463}}}],"partialFingerprints":{"codehealthFindingId/v1":"c7b2ae99423d63f92f97069fc0b1a3ac652fc908335ad7cc43a9dfb9f2e99782"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/editor/Inspector.tsx: packages/koharu/components/editor/Inspector.tsx changed 15 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 37 in Inspector.LayersInspector at line 493. 2 of those changes were fix/bug commits, and the other 13 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/editor/Inspector.tsx\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":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":493}}}],"partialFingerprints":{"codehealthFindingId/v1":"aaa4efb1e616ccd05f2888e356ab4543f4134ef7d42be292ed1f71e549603e5e"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/editor/InferenceControl.tsx: packages/koharu/components/editor/InferenceControl.tsx changed 14 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 26 in InferenceControl.RuntimeSelector at line 86. 7 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/editor/InferenceControl.tsx\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":"packages/koharu/components/editor/InferenceControl.tsx"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"cacd5ebf9d8d01559009505a82805a8aafde0087c06519a8f8e91262de6771c0"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/preferences/SettingsPage.tsx: packages/koharu/components/preferences/SettingsPage.tsx changed 8 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 34 in SettingsPage.SettingsPage at line 58. 1 of those changes was a fix/bug commit, 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/preferences/SettingsPage.tsx\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":"packages/koharu/components/preferences/SettingsPage.tsx"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"67df82ba3e3323436309a4383c68ca5bbc61e3961e01b2a05d4b0a10ff105d24"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/editor/PageRail.tsx: packages/koharu/components/editor/PageRail.tsx changed 6 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 44 in PageRail.PageRail at line 69. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/editor/PageRail.tsx\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":"packages/koharu/components/editor/PageRail.tsx"},"region":{"startLine":69}}}],"partialFingerprints":{"codehealthFindingId/v1":"c4a71b690a0f5ab7e03c7144b69e2993f8c8f2cb48297819f1024d5f47e8eddb"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-renderer/src/text_renderer.rs: crates/koharu-renderer/src/text_renderer.rs changed 12 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 18 in TextRenderer::render_descriptor at line 148. 3 of those changes were fix/bug commits, and the other 9 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-renderer/src/text_renderer.rs\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":"crates/koharu-renderer/src/text_renderer.rs"},"region":{"startLine":148}}}],"partialFingerprints":{"codehealthFindingId/v1":"bfaaddf89b560e3f65fc9f6daa2c916ce94eb25d4ad5054238276d8a8304597c"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-renderer/src/bubble.rs: crates/koharu-renderer/src/bubble.rs changed 10 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 19 in koharu_renderer::bubble::decompose_lobes at line 116. 2 of those changes were fix/bug commits, and the other 8 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-renderer/src/bubble.rs\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":"crates/koharu-renderer/src/bubble.rs"},"region":{"startLine":116}}}],"partialFingerprints":{"codehealthFindingId/v1":"956f013be81ba9df2a2c78becf2495703fe0e0146880bc3bf3626bcf84ddef20"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/controls/FontPicker.tsx: packages/koharu/components/controls/FontPicker.tsx changed 9 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 21 in FontPicker.FontPicker at line 50. 1 of those changes was a fix/bug commit, and the other 8 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/controls/FontPicker.tsx\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":"packages/koharu/components/controls/FontPicker.tsx"},"region":{"startLine":50}}}],"partialFingerprints":{"codehealthFindingId/v1":"85b5563cbcdc25eccd818cd890e27730af466c45931e897eb5b038eb341f31e8"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-translator/src/bin/translate.rs: crates/koharu-translator/src/bin/translate.rs changed 10 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 17 in koharu_translator::bin::translate::prepare_secret at line 107. 2 of those changes were fix/bug commits, and the other 8 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-translator/src/bin/translate.rs\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":"crates/koharu-translator/src/bin/translate.rs"},"region":{"startLine":107}}}],"partialFingerprints":{"codehealthFindingId/v1":"febc85cb3d76c7aebc4866f08c124c72aec7c6cef89cc89d89683d85d41fbd3b"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/app/TitleBar.tsx: packages/koharu/components/app/TitleBar.tsx changed 11 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 15 in TitleBar.TitleBar at line 40. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/app/TitleBar.tsx\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":"packages/koharu/components/app/TitleBar.tsx"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"cb5e47e12c24c78ab96fd69bfad2a0fb06d360593bcc55545d31f2d4570b11d5"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/editor/CanvasOverlay.tsx: packages/koharu/components/editor/CanvasOverlay.tsx changed 7 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 21 in CanvasOverlay.CanvasOverlay at line 33. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/editor/CanvasOverlay.tsx\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":"packages/koharu/components/editor/CanvasOverlay.tsx"},"region":{"startLine":33}}}],"partialFingerprints":{"codehealthFindingId/v1":"6fc9ca376848d5dd6dac209fd54479496f5ad81eac0a3032b8a180ddf7f5b651"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-pipeline/src/stages/inpainting.rs: crates/koharu-pipeline/src/stages/inpainting.rs changed 9 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 16 in Model::run at line 177. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-pipeline/src/stages/inpainting.rs\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":"crates/koharu-pipeline/src/stages/inpainting.rs"},"region":{"startLine":177}}}],"partialFingerprints":{"codehealthFindingId/v1":"67697efdfff5a8c896dfbabf3bb13594492e255e00b1e4b5f16f51c1982e38ff"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/editor/AgentPanel.tsx: packages/koharu/components/editor/AgentPanel.tsx changed 4 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 36 in AgentPanel.AgentPanel at line 38. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/editor/AgentPanel.tsx\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":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"6dcf3cc27a226cf5560e2f81ae9289e3e675be6401d04dd2da92c59d0d3d5bed"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-scene/src/edit.rs: crates/koharu-scene/src/edit.rs changed 7 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 18 in Edit::remove_entity at line 202. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-scene/src/edit.rs\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":"crates/koharu-scene/src/edit.rs"},"region":{"startLine":202}}}],"partialFingerprints":{"codehealthFindingId/v1":"409c198e7593ab42894405bc18c35516f0eb4852402f82b9a2da2b06ce6d1494"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-diffusion/src/enums.rs: crates/koharu-diffusion/src/enums.rs changed 3 times in last 90 days, max cyclomatic complexity 39 in WeightType::try_from at line 69. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-diffusion/src/enums.rs\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":"crates/koharu-diffusion/src/enums.rs"},"region":{"startLine":69}}}],"partialFingerprints":{"codehealthFindingId/v1":"dea4d8c7e4ca05df6fc9622514fbe74ced132a365a79c065c122f389957e63f6"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/app/Updater.tsx: packages/koharu/components/app/Updater.tsx changed 4 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 27 in Updater.Updater at line 31. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/app/Updater.tsx\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":"packages/koharu/components/app/Updater.tsx"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"84549b80d8945c67ff89a96cfaf65dedaecb3c781b3f22613ce68081b433b31e"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-scene/src/schema.rs: crates/koharu-scene/src/schema.rs changed 3 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 34 in koharu_scene::schema::validate_entity at line 75. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-scene/src/schema.rs\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":"crates/koharu-scene/src/schema.rs"},"region":{"startLine":75}}}],"partialFingerprints":{"codehealthFindingId/v1":"fcf7a6419b825a73f4ba773f3bae5cda4b7b73b1f78d3bc682da4596ac10d2b6"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-pipeline/src/stages/ocr.rs: crates/koharu-pipeline/src/stages/ocr.rs changed 6 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 15 in Model::run 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-pipeline/src/stages/ocr.rs\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":"crates/koharu-pipeline/src/stages/ocr.rs"},"region":{"startLine":92}}}],"partialFingerprints":{"codehealthFindingId/v1":"1d1dc52da166c87db6df25817d136acf9af281674a4882aa46e0f83c958b7692"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/controls/OutputPicker.tsx: packages/koharu/components/controls/OutputPicker.tsx changed 5 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 17 in OutputPicker.OutputPicker at line 24. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/controls/OutputPicker.tsx\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":"packages/koharu/components/controls/OutputPicker.tsx"},"region":{"startLine":24}}}],"partialFingerprints":{"codehealthFindingId/v1":"3fb5428c217eb50a697337eebf4e21005d844e9809e717029c0430cb061894f4"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: packages/koharu/components/controls/ModelPicker.tsx: packages/koharu/components/controls/ModelPicker.tsx changed 3 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 27 in ModelPicker.ModelPicker at line 18. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/controls/ModelPicker.tsx\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":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"ec68daf11e1ab0109ae00a498ddb074d7642feeafb98058fd63823cb8bfb1a48"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-translator/src/provider.rs: crates/koharu-translator/src/provider.rs changed 5 times in last 90 days, max cyclomatic complexity 16 in ProvidersConfig::from_entries at line 90. 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-translator/src/provider.rs\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":"crates/koharu-translator/src/provider.rs"},"region":{"startLine":90}}}],"partialFingerprints":{"codehealthFindingId/v1":"2aac20b821bd80c356559e61b4754d0f1a08896593a33037ae1940bc2ae9b946"}},{"ruleId":"D15","level":"warning","message":{"text":"Hotspot: crates/koharu-psd/src/document.rs: crates/koharu-psd/src/document.rs changed 3 times in last 90 days, and the most complex body those changes touched has cyclomatic complexity 20 in koharu_psd::document::build 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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-psd/src/document.rs\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":"crates/koharu-psd/src/document.rs"},"region":{"startLine":88}}}],"partialFingerprints":{"codehealthFindingId/v1":"149bf32ad79b98a51bbbfc029aa57731477804cb999341681baa0147540e95c6"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-runtime/src/runtime/packages/torch.rs: crates/koharu-runtime/src/runtime/packages/torch.rs changed 25 times in last 90 days and 18 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 Torch::discover at line 116), 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(runtime): update Torch to v2.13.0.6\u201D; \u201Cfix(runtime): update native dependencies\u201D; \u201Cfix(runtime): update Torch to v2.13.0.3\u201D; \u201Cfix(runtime): update Torch release\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-runtime/src/runtime/packages/torch.rs\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":"crates/koharu-runtime/src/runtime/packages/torch.rs"},"region":{"startLine":116}}}],"partialFingerprints":{"codehealthFindingId/v1":"a94b48ff7e6cdc512cee69669e675e16de410541866d1f7dcc142a32227c3424"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-runtime/src/runtime/packages/rocm.rs: crates/koharu-runtime/src/runtime/packages/rocm.rs changed 19 times in last 90 days and 11 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 3 (its worst body is koharu_runtime::runtime::packages::rocm::wheel_platform at line 133), 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(runtime): align ROCm targets with llama.cpp\u201D; \u201Cfix(runtime): drop unsupported gfx1250\u201D; \u201Cfix(runtime): use exact ROCm library paths\u201D; \u201Cfix(runtime): update ROCm to 10.0\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-runtime/src/runtime/packages/rocm.rs\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":"crates/koharu-runtime/src/runtime/packages/rocm.rs"},"region":{"startLine":133}}}],"partialFingerprints":{"codehealthFindingId/v1":"17a9793c76efa9c4ba784227402479e55d82d68a778a27ee59420d26becffbbf"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-runtime/src/hardware/mod.rs: crates/koharu-runtime/src/hardware/mod.rs changed 7 times in last 90 days and 6 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 10 (its worst body is Hardware::probe at line 26), 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(runtime): improve accelerator detection\u201D; \u201Cfix(runtime): reject unsupported CUDA architectures\u201D; \u201Cfix: runtime discover\u201D; \u201Cfix: support multiple GPUs\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-runtime/src/hardware/mod.rs\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":"crates/koharu-runtime/src/hardware/mod.rs"},"region":{"startLine":26}}}],"partialFingerprints":{"codehealthFindingId/v1":"bec135213f0abc59f477bb7c3fd085d2a9148df1c2d43c597d8eb86539a68d18"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-runtime/src/runtime/graph.rs: crates/koharu-runtime/src/runtime/graph.rs changed 6 times in last 90 days and 5 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 5 (its worst body is Component::dependencies at line 27), 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(runtime): improve accelerator detection\u201D; \u201Cfix: runtime discover\u201D; \u201Cfix: support multiple GPUs\u201D; \u201Cfix: only enable bf16 when compute capability \u003E= 80\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-runtime/src/runtime/graph.rs\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":"crates/koharu-runtime/src/runtime/graph.rs"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"1d4ae1f4d692d0855c5fff2e0f86863e4235363949243e7199b1cdc97c35d87e"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-runtime/src/runtime/loader.rs: crates/koharu-runtime/src/runtime/loader.rs changed 5 times in last 90 days and 5 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 2 (its worst body is koharu_runtime::runtime::loader::open at line 28), 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(runtime): improve accelerator detection\u201D; \u201Cfix(runtime): load ROCm libraries globally\u201D; \u201CFix AppImage runtime library exclusions\u201D; \u201CFix Linux Torch runtime bundling\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-runtime/src/runtime/loader.rs\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":"crates/koharu-runtime/src/runtime/loader.rs"},"region":{"startLine":28}}}],"partialFingerprints":{"codehealthFindingId/v1":"5098a400d0ea9923f5b0fa7a1044ac5cd8a531ef61fc3c3bd6db94d1c5d67057"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs: crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs changed 5 times in last 90 days and 3 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 4 (its worst body is Mlp::new at line 1139), 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(ml): align RF-DETR inference with upstream\u201D; \u201Cfix: support multiple GPUs\u201D; \u201Cfix: only enable bf16 when compute capability \u003E= 80\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs\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":"crates/koharu-ml/src/koharu_layout_rfdetr_seg_2xl/model.rs"},"region":{"startLine":1139}}}],"partialFingerprints":{"codehealthFindingId/v1":"20b386694db44ddec8c8d66174126dbbd3ab854348db2aab6406f63204c79f30"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: packages/koharu/components/app/WindowChrome.tsx: packages/koharu/components/app/WindowChrome.tsx changed 5 times in last 90 days and 3 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 8 (its worst body is WindowChrome.WindowControls at line 32), 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(app): restore frameless window resizing\u201D; \u201Cfix(macos): titlebar style\u201D; \u201Cchore(dev): fix macos development\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- packages/koharu/components/app/WindowChrome.tsx\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":"packages/koharu/components/app/WindowChrome.tsx"},"region":{"startLine":32}}}],"partialFingerprints":{"codehealthFindingId/v1":"f3464078da115995791f47d387c97e97a5a462abb9a95c38cb44b86edcc7016b"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-ml/src/paddle_ocr_vl/model.rs: crates/koharu-ml/src/paddle_ocr_vl/model.rs changed 5 times in last 90 days and 3 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 9 (its worst body is Model::get_rope_index at line 237), 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: support multiple GPUs\u201D; \u201Cfix: only enable bf16 when compute capability \u003E= 80\u201D; \u201CFix tests\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-ml/src/paddle_ocr_vl/model.rs\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":"crates/koharu-ml/src/paddle_ocr_vl/model.rs"},"region":{"startLine":237}}}],"partialFingerprints":{"codehealthFindingId/v1":"2aaaab299d91f8f3cc0c88f8b64e410bd5cecc5b3510763ecaf19e96a9e273e8"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-ml/src/backend.rs: crates/koharu-ml/src/backend.rs changed 4 times in last 90 days and 3 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 9 (its worst body is Device::try_into_device at line 10), 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: disable MIOpen on Windows\u201D; \u201Cfix(ml): disable MIOpen on Windows gfx10\u201D; \u201Cfix(ml): select precision by device capability\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-ml/src/backend.rs\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":"crates/koharu-ml/src/backend.rs"},"region":{"startLine":10}}}],"partialFingerprints":{"codehealthFindingId/v1":"584fe345bd1bac5e87557f6773338bbddaf85d571f2bf36bd4724ecf562c3d62"}},{"ruleId":"D15","level":"warning","message":{"text":"Repeated repair: crates/koharu-runtime/src/runtime/mod.rs: crates/koharu-runtime/src/runtime/mod.rs changed 4 times in last 90 days and 3 of those changes were fix/bug commits, so repair is the majority of this file\u0027s churn. Its max cyclomatic complexity is 7 (its worst body is Runtime::plan at line 73), 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(runtime): improve accelerator detection\u201D; \u201Cfix: runtime discover\u201D; \u201Cfix: support multiple GPUs\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-07-02..2026-09-30, the 90 days ending at the analysed commit. Reproduce with \u0060git log --since=\u00272026-07-02 00:59:19 \u002B09:00\u0027 --until=\u00272026-09-30 00:59:19 \u002B09:00\u0027 --full-history --no-merges -- crates/koharu-runtime/src/runtime/mod.rs\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":"crates/koharu-runtime/src/runtime/mod.rs"},"region":{"startLine":73}}}],"partialFingerprints":{"codehealthFindingId/v1":"817ae7318ac5ce0a6ed2ef147676cd73d681d449532aca201b4bef8e090dc348"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO lsptrip None is acutally not quite the origignal behavior of this function, \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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":236}}}],"partialFingerprints":{"codehealthFindingId/v1":"06419eef53653155f999af98b9add0af6454d0b8ad1177eface53f1046fa1b50"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO lsptrip None is acutally not quite the origignal behavior of this function, \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":"crates/koharu-llama/src/model.rs"},"region":{"startLine":261}}}],"partialFingerprints":{"codehealthFindingId/v1":"6a890f5545fa27df586f12c130d003d2339ffb2f555d797bc53be2cd88f8d314"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Support logging this to stdout directly via options? \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":"crates/koharu-llama/src/log.rs"},"region":{"startLine":241}}}],"partialFingerprints":{"codehealthFindingId/v1":"580c4c790ac26bdbd05153da37f62aeae790ba66e658d09a062d97d0038e5dcd"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Reinitialize the state to support calling send_logs_to_tracing multiple times. \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":"crates/koharu-llama/src/lib.rs"},"region":{"startLine":555}}}],"partialFingerprints":{"codehealthFindingId/v1":"2da5f2fcfc353b33afbc114e3cbe4488bdbe9dcdbe68136e04635b692014ee0d"}},{"ruleId":"D17","level":"warning","message":{"text":"HackComment: // Hacky parser for the npy header, a typical example would be: \u2014 a workaround marked in source: record what it is compensating for and what would allow its removal (the upstream fix, the API it is waiting on, the invariant it restores), so the next reader can judge whether it is still needed rather than rediscovering why it is there."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-torch/src/tensor/npy.rs"},"region":{"startLine":78}}}],"partialFingerprints":{"codehealthFindingId/v1":"8139c738a14ef5e5bb729a0ebde900ea5a76c9048deaf97cbc006feab692cc8a"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Try to remove this intermediary copy. \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":"crates/koharu-torch/src/tensor/convert.rs"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"0f44c666f93980cd586a8eb4fc02a17aadf689dd6de7cfc0b5bca35400618068"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Try to remove this intermediary copy. \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":"crates/koharu-torch/src/tensor/convert.rs"},"region":{"startLine":51}}}],"partialFingerprints":{"codehealthFindingId/v1":"a2ac90d15b32d9aa463da8361753d474b26467c4239f9deba9dc6a51485e6fe5"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: use swiglu. \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":"crates/koharu-torch/src/vision/dinov2.rs"},"region":{"startLine":5}}}],"partialFingerprints":{"codehealthFindingId/v1":"e463ff0c1614b5314b04cbeef1ac7be5c66f4664e96d1cc29188328589310e14"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // Eq or Hash out of the box in rust. TODO: improve this? \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":"crates/koharu-torch/src/wrappers/jit.rs"},"region":{"startLine":29}}}],"partialFingerprints":{"codehealthFindingId/v1":"bdc402afecc258ff1dfa77183eea4e937f7f8a4a8c077d57f0a9f217f19df4ad"}},{"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":"D22","level":"warning","message":{"text":"Redundant cancellation state checking. \u0060cancel()\u0060 is an action, but \u0060cancelled()\u0060 and \u0060is_cancelled()\u0060 appear to serve the same purpose of checking the cancellation state. Having two methods with nearly identical names for the same intent is confusing.: Remove \u0060cancelled()\u0060 and keep only \u0060is_cancelled()\u0060 (or vice versa, depending on naming convention preference, but \u0060is_cancelled\u0060 is more idiomatic for boolean checks). Ensure \u0060cancel()\u0060 is the only mutation method. (signatures: koharu_agent.control.Control.cancel() | koharu_agent.control.Control.cancelled() | koharu_agent.control.Control.is_cancelled())"},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"180e34fdb230c1117dafdb282a070219b4a071d35df1a7308d72ef09306fc39c"}},{"ruleId":"D26","level":"note","message":{"text":"Projects may be oversized for their cohesion: 1 of 24 project(s) overshoot their size bounds, lowering Project Cohesion to 9.2/10. The most over is \u0060crates/koharu-ml\u0060 (30203 LoC, 177 public types across 26 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":"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":"d5b8f11381d61a58459ffc7c595fd52d09c03c711894a63a9578043d2338412b"},"taxa":[{"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":"9603565aa6069e0abcf535dcde33980e7f55a15fa6af1c7abedeeffe7f52bf08"},"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":"6a6f03be5613cbb491b7dac69a0d333ac0ee7c24ffb528b33846b691c06fad82"},"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":"1f4038f9c29318e4cabfcfe5282a24e2b2a58c8cdd0709749bf69fc3092cd1a8"},"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":"440d3c0e011476046a9eb8879e5e8d3a0e9e13278a7d885142ea8cbd0fa41aaf"},"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":"3b20c9e3f5f07cd9ed7d38e1ad9e3677b956fa28fa72534ce39bdba4769b42e6"},"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":"41f06f55eb1c07fcd6f7f67b46e03fad264e898d294819f2899e8b5da48a41ef"},"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":"da0c6155e689848e5e9757312158e9207a2a0fe7de3a0a8a86bac8a6d7db6f63"},"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":"2cfc2220aa931cf73ba3d82f9f2364faf7e3517c5221a5a41d89b37ac8ef43a6"},"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":"0b3acdcce37a886ff1823abb9d09d8d24539d6b61ed9a833d9c6a0580803c9b2"},"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":"23e4b449ebd7cd14e8fa96df23ef070f6e710fa91a2c7bade291ff12d107feff"},"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":"3ed93a2995ee2f6ac2e3b70756bc2376c5d26e6317fb5ca5361dc6f503eb1bdf"},"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":"693249723480b7d3690fbc9002e4acaa2fd5c4f5277d52948268b51530c89d1e"},"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":"77c894c5378a258ec83bc5057e004d6f7c7c1f4a56f817e859d1a5b95a2cd2e9"},"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":"8018fa76f9798f58c2d54888e01f82e01d93750f0c34580eea02821510c2b5a4"},"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":"bad26329f8f87fa0a3cd5d8d28df2c5ac35be85b3b3627ff94de168ef3e43704"},"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":"17f54fb2cbcb3a3ac79db6ee63e1de2d72883c1f15bc6adcd1384335d0b9f95e"},"taxa":[{"id":"CWE-494","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":"b3d76b3692cc552ac7722711a9f84658a3597fafb073da4e8d1a2bef83fc4fe7"},"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":"e28424fedc360265a4515c4fa929e6bf297e08d2621025a4f057e7095c665354"},"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":"2cc074ca03002738cd3cc9ccc9f8b9a681176496735869b425d81b66d83a485b"},"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":"e42ba3d84e3bde183a4fd2d72f5ca0c075fc1af571a674fa31b9ba3cb933813c"},"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":"29294ca326979f510705057e645b335d4491ecf2258b65b8f6ea7be71e750506"},"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":"a39c59049cee5c1b3ea37164cca414874aae2c207a1cca01e88a04867cce1c36"},"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":"9ca50b305686af151b0ee0299638df53248e224c3935e1e35c2758f1a93ccb9b"},"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":"1a61de973287e11903c47a85caed2081faa401ffcc1b0080d54d5a3747008fb5"},"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":"48577be74542d502d3d8d393cc98128a1ed043613a51f5953fb617351276f0b6"},"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":"5199b40473c2e6049b809781f3fbbdb6c92d1f89ad0ccdca0473657732f0567a"},"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":"bb09b1305160c02ecb0571b1c0ec4ebef7c0dc723f6181413d1cc4f02da79f53"},"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":"efc654b437df2610286c5e75ee6d226261e8befdc3bd1a68a15cb27bf096208c"},"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":"43b55e907d0387cea364f662ca9989568d0806fb1d26fb3813a996504013e977"},"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":"963e69c5d253c4d019b037c3c9e5f4dc8d49148eebd1065c94590dbfffeec24b"},"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":"41020232c5506bc94c6903fa6bc4e7b9f5c2b2bb5d5e86f087bf10b5f46088de"},"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":"dbfe6b7b9f24aa4872a89a9a83cf0b3973438ce3ce0122ef128ff8205aa23633"},"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":"f18dbc4e23b4373733337fe0984adb05ef09633be3912f3ab3e2f4d1030a1be5"},"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":"c4d9082b71f316e66e7064a3a7797823389d9690c2801bd20a4a909c851fa115"},"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":"09a377505305bbe2771b3caec6959c6f24eea1f1d9e653b40c5c2b49856b8dbb"},"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":"55a9feee9774121f2f799f78815f48406c361b20f27615a6b6c4aa576c312700"},"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":"86f2130772dcaee755750b649d5b6d962098cc8a941f085f680bdabcb3329507"},"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":"929889218fe2adba5577454da6e2fffb54a21f38de62c057ea511bf22abbc080"},"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":"9648f976e1105c5a5a02ca12fe3222fb4a5a8dc8a16c4a2de998c6ccf2a532d5"},"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":"5bb95637f60f4b26090e465b08270f3021dbe24290e06a60f7188dbfd0ed52cc"},"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":"9ee6637b0f9faf210013a2d0b3f3380aaa1587907f2badfd2192d4dc58505774"},"taxa":[{"id":"CWE-79","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D29","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":"63778fa624412affc09bbae17ed4e235deee47c1e8fbbd49594e08c2f223c987"},"taxa":[{"id":"CWE-522","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-552","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}}]},{"ruleId":"D29","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":"c49f1fe2b91a6ac458d41a1ea37da7691e2d7406affd79431f9edf100b945480"},"taxa":[{"id":"CWE-310","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":"e2c844ee18ac9a641ea30dc49cdcfbf1abe8e80273fa18f842dc69cb93773d24"},"properties":{"dependency":{"package":"js-yaml","version":"4.3.1","advisory":"[GHSA redacted]","aliases":["[CVE redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"e62d3655cf31b6a3e67df07d4bd9610cb6dd96fa82e496c81ba0caf40c9eecdc"},"properties":{"dependency":{"package":"brace-expansion","version":"1.1.18","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[CVE redacted]","[GHSA redacted]","[GHSA redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"938fc9e55a58143afa9cc058d80347ca3724a0a6b4cb9d63f6bb89c965b5699f"},"properties":{"dependency":{"package":"brace-expansion","version":"5.0.9","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[CVE redacted]","[GHSA redacted]","[GHSA redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"86dcbeeacc84de4eda929ac60f5afa000277cf54835e529a9bafc5e9a9c7c1fe"},"properties":{"dependency":{"package":"extract-zip","version":"2.0.1","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[GHSA redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"50fb1aec7c3044303e0012b944d9ecb9d2db6dd856e4cc53aa747b03aebe7e01"},"properties":{"dependency":{"package":"adm-zip","version":"0.6.0","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[CVE redacted]","[CVE redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]","[GHSA redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"9d7e2f5e4e133844171b9412d83e460349b527553e21e4eb5d4f1e038ed6831f"},"properties":{"dependency":{"package":"atomic-polyfill","version":"1.0.3","advisory":"RUSTSEC-2023-0089","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":"5922a5beeab5ac077ffb485a783ad545036e79b56239f83da38f3fb84336b113"},"properties":{"dependency":{"package":"paste","version":"1.0.15","advisory":"RUSTSEC-2024-0436","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":"a42ca7416279f52063a649dbc505340ffc7c7a5727193e601ae1de8d6db75e3d"},"properties":{"dependency":{"package":"fxhash","version":"0.2.1","advisory":"RUSTSEC-2025-0057","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":"c356dafc2d2f879539349393edf47ceccc3e0771bf15e4dae816abd8594fd25c"},"properties":{"dependency":{"package":"ttf-parser","version":"0.25.1","advisory":"RUSTSEC-2026-0192","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":"d44a25813d8a9e18eb42fa5350a972c298292d454155c6472f091bfb670363df"},"properties":{"dependency":{"package":"rand","version":"0.7.3","advisory":"RUSTSEC-2026-0097","aliases":["[GHSA 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":"3035e85b1a9c0c33df37dc333f490439b3fa52aefda24a401e2e75a177cdf778"},"properties":{"dependency":{"package":"rustls","version":"0.23.44","advisory":"RUSTSEC-2026-0285","aliases":["[GHSA 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":"2263a75da41737bd41be73a206bdf4145a5cf182e4ae843b420ad4dbcb950cf4"},"properties":{"dependency":{"package":"qs","version":"6.14.2","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[CVE redacted]","[GHSA redacted]","[GHSA redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"25e5c1165ef53337d859657ca78c3b9c3c71e4455baad474b86b51e616d34f40"},"properties":{"dependency":{"package":"ip-address","version":"10.7.0","advisory":"[GHSA redacted]","aliases":["[CVE redacted]","[CVE redacted]","[GHSA redacted]"],"reachability":{"kind":"not-imported"}}}},{"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":"ba9166eedc0226c88cc68c8f0a4fceeb9144c223d00cecc248ea6fe149c84136"},"properties":{"dependency":{"package":"fast-uri","version":"3.1.7","advisory":"[GHSA redacted]","aliases":["[CVE redacted]"],"reachability":{"kind":"not-imported"}}}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling clique: diffusion.rs, llama.rs, torch.rs: 3 files \u2014 \u0060crates/koharu-runtime/src/runtime/packages/diffusion.rs\u0060, \u0060crates/koharu-runtime/src/runtime/packages/llama.rs\u0060, \u0060crates/koharu-runtime/src/runtime/packages/torch.rs\u0060 \u2014 all change together with no explicit dependency: a fully-connected co-change clique, not 3 separate couplings. They share one concern (thin parallel siblings over a common abstraction), so extract the shared part into ONE unit and the whole clique\u0027s coupling clears at once \u2014 you do not need to break each pair individually."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-runtime/src/runtime/packages/diffusion.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f5a8d37db361235f08cc66a8c19af134db940da287057c36cda2897df5449433"}},{"ruleId":"D35","level":"warning","message":{"text":"Change coupling: mod.rs \u2194 mod.rs: \u0060crates/koharu-ml/src/comic_text_detector/mod.rs\u0060 and \u0060crates/koharu-ml/src/lama/mod.rs\u0060 change together 50% of the time (7 of the 14 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) with no explicit dependency \u2014 the edge is real but nothing declares it. Read the pair before acting: if one registers itself into the other through a hook or an initialiser, the missing dependency is DELIBERATE \u2014 the registration is the link, and it is meant not to be an import \u2014 and the thing to add is a comment on each side naming the other, not a merge; if they simply belong together, co-locate them; if neither holds, the coupling is hidden and worth breaking. You can check this without leaving the row: of the 7 shared commits counted here, the most recent 3 are \u0060680311ce\u0060 Use explicit device dtype conversions when loading models; \u00606f1494af\u0060 perf: improve inpainting (at that commit the files were still \u0060koharu-ml/src/comic_text_detector/mod.rs\u0060 and \u0060koharu-ml/src/lama/mod.rs\u0060); \u00603e4b068e\u0060 refactor(ml): shared TextRegion/TextDirection types for pipeline engines (at that commit the files were still \u0060koharu-ml/src/comic_text_detector/mod.rs\u0060 and \u0060koharu-ml/src/lama/mod.rs\u0060) \u2014 run \u0060git show\u0060 on any of them."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-ml/src/comic_text_detector/mod.rs"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f7e397c1ab2784d7e8324126097748426c5cac6e9f2723a2d666364412d9cee7"}},{"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":"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":"cc682cd7167c2c692b624e54d299d32b2ea2b5b3eb72f5516f0dfab9c0ce991d"}},{"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":"23242335439540a7f0752c9def5c038b557d1cb0f8e6f7b448b5f69adcab3c1d"}},{"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":"AC2","level":"warning","message":{"text":"\u003CSelect\u003E field component without a label: This UI-library field component has no label / aria-label / aria-labelledby / id / name, and nothing it renders carries one either. The text inside it does not stand in for one: these compound APIs render a trigger with role=\u0022combobox\u0022 (or role=\u0022button\u0022), and those roles take their name from the AUTHOR, not from their content \u2014 so the words on screen never reach the accessibility tree as a name. Name it whichever way this library supports: a label prop, an aria-label, or an id on the rendered control with a \u003Clabel htmlFor\u003E pointing at it. For a group of controls, name the group itself (aria-label, or a fieldset with a legend) \u2014 labelling each item leaves the set unnamed."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/SettingsPage.tsx"},"region":{"startLine":277}}}],"partialFingerprints":{"codehealthFindingId/v1":"37473ed212b523cd4250cce0bcca2e1b8d44e1d73ac07081726415e31e19b2cd"}},{"ruleId":"AC3","level":"error","message":{"text":"Document without a \u003Ctitle\u003E: A page with no \u003Ctitle\u003E gives no name in the tab, history or screen-reader page list. Declare it the way this router does \u2014 Next App Router \u0060export const metadata = { title }\u0060, React Router/Remix \u0060export function meta\u0060, TanStack Start \u0060head\u0060, or a Helmet/Head component."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/global-error.tsx"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"eadd359a7ed11147f7acf12b58f92db48aac565fddd70d7780f920da3552bc43"}},{"ruleId":"AC3","level":"warning","message":{"text":"Page without a main landmark: No \u003Cmain\u003E (or role=\u0022main\u0022) means no \u0022skip to content\u0022 target and a weaker landmark map. Wrap the primary content in \u003Cmain\u003E."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/layout.tsx"},"region":{"startLine":24}}}],"partialFingerprints":{"codehealthFindingId/v1":"28b0de2ce134470e69f59bfe891fc6593afb77d50c335823fcc3751bd3907669"}},{"ruleId":"AC4","level":"error","message":{"text":"Focusable \u003Cdiv\u003E isn\u0027t keyboard-operable: This \u003Cdiv\u003E carries tabIndex={0}, so it is in the tab order and a keyboard user will land on it \u2014 but its only behaviour is bound to wheel, pointerdown, pointerup and pointermove, pointer gestures a keyboard cannot produce, and there is no key handler. The tab stop leads nowhere. Bind the same behaviour to a key handler, or provide an equivalent control elsewhere. Give it a role as well, so assistive tech can announce what it is."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":532}}}],"partialFingerprints":{"codehealthFindingId/v1":"702772347c96c55b34847470f4b2178b4988124098c65819ab27d3f5e7fcc084"}},{"ruleId":"AC4","level":"error","message":{"text":"Click handler on a non-interactive \u003Carticle\u003E: A click handler on a plain element isn\u0027t keyboard-operable. Use a \u003Cbutton\u003E, or add role \u002B tabIndex={0} \u002B a key handler."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/PageRail.tsx"},"region":{"startLine":472}}}],"partialFingerprints":{"codehealthFindingId/v1":"24fe4aa10b29948a177bae0b4679637495050af9da3cb97b819e3ad0d4f5fc35"}},{"ruleId":"AC7","level":"warning","message":{"text":"Accessibility enforcement below the top rung: No accessibility enforcement found \u2014 your oxlint config does not list the \u0060jsx-a11y\u0060 plugin (its a11y rules are off until it does) and there\u0027s no axe/pa11y/Lighthouse in tests or CI. Start by enabling that plugin to catch issues at author time. What was searched, so you can tell an absence from a miss: the 97 markup file(s) this pass actually assessed, the linter configuration checked in beside them, and this repository\u0027s test and CI files \u2014 matched by name against the accessibility checkers this dimension carries. An audit run outside the repository, a hosted scanner, or a check whose name is not one of those, is not seen here."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"d46019b86eff5ddad9db289e6802ac158899e980df54c34f67722442787ead62"}},{"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":"M4","level":"note","message":{"text":"README/code drift: README omits the Tauri frontend project \u2014 reported by the model that read the README against this repository; no term search was run for this one, so nothing here has been checked against the tree. Treat it as a reading to confirm, not as a measured contradiction: verify it against the code before acting on it, and if the footprint it describes does exist, this row is wrong."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"9815f4cdeefe8eda47aa6386dd1354498d9a9fd47318063bac079e5b746e4e56"}},{"ruleId":"M4","level":"note","message":{"text":"README/code drift: omits the UI/bridge package (packages/ui) \u2014 reported by the model that read the README against this repository; no term search was run for this one, so nothing here has been checked against the tree. Treat it as a reading to confirm, not as a measured contradiction: verify it against the code before acting on it, and if the footprint it describes does exist, this row is wrong."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"5064b83cf60cde077a4b154cee87a859cfe361317125c9e0004011e56506e247"}},{"ruleId":"M4","level":"note","message":{"text":"README/code drift: omits the agent-based workflow project \u2014 reported by the model that read the README against this repository; no term search was run for this one, so nothing here has been checked against the tree. Treat it as a reading to confirm, not as a measured contradiction: verify it against the code before acting on it, and if the footprint it describes does exist, this row is wrong."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"c116bfc05d48bd3ca2ffad777f24e2bb1176018b357d9aec701a0b08c949e32f"}},{"ruleId":"P2","level":"note","message":{"text":"Logging is not universal: Only 16/18 runnable modules use logging (modules with no entry point or server are excluded \u2014 they are libraries a runnable module hosts). Silent: \u0060crates/koharu-bindgen\u0060, \u0060packages/koharu\u0060."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"91a94e21d6d4d0e6a2003f94505b0b02b157176f0b24c4820f3f82b4447a97fe"}},{"ruleId":"P4","level":"note","message":{"text":"No release approval gate: The release is automated and no gate that pauses it for a human is DECLARED IN THIS REPOSITORY\u0027S PIPELINE FILES. What was read: every file under \u0060.github/workflows/\u0060, \u0060.forgejo/workflows/\u0060, \u0060.gitea/workflows/\u0060, \u0060.azuredevops/\u0060 and \u0060.azure-pipelines/\u0060, plus \u0060.gitlab-ci*\u0060 and \u0060azure-pipelines*\u0060 \u2014 with comment text stripped, so documenting a gate is not declaring one. What would have counted: GitLab\u0027s \u0060when: manual\u0060, CircleCI\u0027s \u0060type: approval\u0060, an Azure \u0060ManualValidation@\u0060 task or an \u0060approvals:\u0060 block, a Jenkins \u0060input\u0060 step, a \u0060uses:\u0060 step naming an approval action, an \u0060environment:\u0060 paired with \u0060reviewers\u0060 / \u0060required_reviewers\u0060 / \u0060protection\u0060 / \u0060wait-timer\u0060 / \u0060deployment_branch_policy\u0060, a draft-release step, a \u0060workflow_dispatch\u0060 promotion, or a release-event gate. \u2605 What this cannot see, because none of it is a file: a GitHub environment whose required reviewers are configured in repo SETTINGS, a branch protection rule, or an organisation deployment policy \u2014 all of them real, enforced gates that live outside the repository. If yours is one of those, this row is wrong and nothing in the tree could have told us. Otherwise: whatever the release trigger points at is published to users unreviewed, so a mistagged or unverified commit ships and the only remedy is a follow-up release."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"6c11e2dbd483b4b423b95ac61bfcc7af1d55351b28a20d2b1105b31cfe38cc60"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplication concentrated across 5 sibling directories (8 clone groups): 8 duplicated blocks under packages/koharu/components/ have copies in at least two of the sibling directories app, controls, editor, preferences, start \u2014 3 of them are reported below, and 5 are counted here but not reported individually: those copies match on shape but no longer clear R10\u0027s bar for an individually reported row \u2014 either they kept neither their own names nor their values, or what was copied is too small to stand on its own (it reports near-exact duplication only, and only of substantial extent). What this row states is the concentration, which the detector measured over all 8 and which does not depend on how exactly each block\u0027s copies still match. That concentration is one structural fact, not 8 local ones: the siblings replicate behaviour none of them owns, which is the shape of a missing shared module \u2014 a common library every sibling imports \u2014 rather than 8 separate extractions. Check first whether the siblings are deliberately standalone deliverables (scaffold templates, demo apps that must stay copy-pasteable); where they are, the duplication is the design and the per-block rows are the ones to act on."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/OutputPicker.tsx"},"region":{"startLine":130}}}],"partialFingerprints":{"codehealthFindingId/v1":"4af0aff3b5df1e8136fde73acf8aa38c864ac961e1d3452cbe7999781cdb1974"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block with local edits (203 matched lines \u00D7 2 locations): packages/ui/src/components/context-menu.tsx:37 \u00B7 packages/ui/src/components/dropdown-menu.tsx:28 \u2014 the two spans are one implementation copied and then locally edited \u2014 707 tokens are still identical, in the same order in both files, with only local edits between them. The copies have already begun to drift, which is this row\u0027s finding: an edit made to one and not the other changes behaviour silently. Diff the two spans first to learn what genuinely differs, then extract the shared core into one module both sites use, passing the differences in as parameters \u2014 or, if one copy exists only because the other could not be imported from its context, make one of them the single source the other is generated or re-exported from. If one copy is no longer reachable, delete it rather than letting it shadow the live one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/context-menu.tsx"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"df44487d5e783736c1b1c4626819daf3eed3b6c0d1b99280de9f5213a41b9ace"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block with local edits (76 matched lines \u00D7 2 locations): packages/ui/src/components/dialog.tsx:10 \u00B7 packages/ui/src/components/sheet.tsx:10 \u2014 the two spans are one implementation copied and then locally edited \u2014 336 tokens are still identical, in the same order in both files, with only local edits between them. The copies have already begun to drift, which is this row\u0027s finding: an edit made to one and not the other changes behaviour silently. Diff the two spans first to learn what genuinely differs, then extract the shared core into one module both sites use, passing the differences in as parameters \u2014 or, if one copy exists only because the other could not be imported from its context, make one of them the single source the other is generated or re-exported from. If one copy is no longer reachable, delete it rather than letting it shadow the live one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/dialog.tsx"},"region":{"startLine":10}}}],"partialFingerprints":{"codehealthFindingId/v1":"bb836c9ca7a07fe0fd69f1ff4d746c60019fc5098ff6ff3d0abb279830a176e1"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block with local edits (54 matched lines \u00D7 2 locations): packages/ui/src/components/combobox.tsx:93 \u00B7 packages/ui/src/components/select.tsx:64 \u2014 the two spans are one implementation copied and then locally edited \u2014 240 tokens are still identical, in the same order in both files, with only local edits between them. The copies have already begun to drift, which is this row\u0027s finding: an edit made to one and not the other changes behaviour silently. Diff the two spans first to learn what genuinely differs, then extract the shared core into one module both sites use, passing the differences in as parameters \u2014 or, if one copy exists only because the other could not be imported from its context, make one of them the single source the other is generated or re-exported from. If one copy is no longer reachable, delete it rather than letting it shadow the live one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/combobox.tsx"},"region":{"startLine":93}}}],"partialFingerprints":{"codehealthFindingId/v1":"7b331424812865ebc9120be727a798438c56cdb26dd8d508f8ee774181a0b526"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (33 lines \u00D7 3 locations): packages/ui/src/components/drawer.tsx:163 \u00B7 packages/ui/src/components/popover.tsx:46 \u00B7 packages/ui/src/components/sheet.tsx:79 \u2014 the 3 copies are spread across 3 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/drawer.tsx"},"region":{"startLine":163}}}],"partialFingerprints":{"codehealthFindingId/v1":"4c5abdcbf6504eabbf4cf3d5b60cfc49c5ef1b37c06e607d1f15d4483692c840"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (31 lines \u00D7 2 locations): packages/ui/src/components/empty.tsx:34 \u00B7 packages/ui/src/components/item.tsx:91 \u2014 the 2 copies are spread across 2 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/empty.tsx"},"region":{"startLine":34}}}],"partialFingerprints":{"codehealthFindingId/v1":"9870e2a6f4e43fd398ce8fe32eeaa786fb35b59aa5b91d68c69f53b699d8b5e7"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (31 lines \u00D7 2 locations): packages/ui/src/components/field.tsx:10 \u00B7 packages/ui/src/components/message.tsx:5 \u2014 the 2 copies are spread across 2 files, and the SHAPE of this repetition could not be determined. It is not a run of declarations, a listing, a declaration header, a type body or a slice through a construct \u2014 and it was not measured as a run of executable statements either, so this row cannot tell you whether a function can stand where these lines are. Read the two spans before acting, because the move is opposite in the two cases. Where they are statements, the ordinary answer holds: give the shared part one home and call it from each site. Where they turn out to be declarations, a literal\u0027s entries, or the cases of an enumeration, there is no call site to call anything from, and collapsing them would delete what each copy pins \u2014 a shared base type, a generated set, or one exported constant each site refers to is the move instead, and sometimes the honest answer is that there is nothing to extract at all. Reported because the copies drift apart the first time only one of them is edited, which is true whichever of those they are."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/field.tsx"},"region":{"startLine":10}}}],"partialFingerprints":{"codehealthFindingId/v1":"afcc74d7ba8df2a9d65c82a0b1c8e68260e53b08cdc2c4473367faea7c840cf5"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (30 lines \u00D7 2 locations): packages/ui/src/components/context-menu.tsx:155 \u00B7 packages/ui/src/components/menubar.tsx:109 \u2014 the 2 copies sit in sibling files in one directory, so check first whether one of them (or an existing module there) already owns this behaviour and the others should call it; otherwise extract it into one module in that directory and have each site call it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/context-menu.tsx"},"region":{"startLine":155}}}],"partialFingerprints":{"codehealthFindingId/v1":"945edaf8f464dfe00619b5cfc17debf3b95328daa6fc99679b926ec112b1093d"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (30 lines \u00D7 2 locations): packages/ui/src/components/popover.tsx:8 \u00B7 packages/ui/src/components/tooltip.tsx:20 \u2014 the 2 copies are spread across 2 files, and the SHAPE of this repetition could not be determined. It is not a run of declarations, a listing, a declaration header, a type body or a slice through a construct \u2014 and it was not measured as a run of executable statements either, so this row cannot tell you whether a function can stand where these lines are. Read the two spans before acting, because the move is opposite in the two cases. Where they are statements, the ordinary answer holds: give the shared part one home and call it from each site. Where they turn out to be declarations, a literal\u0027s entries, or the cases of an enumeration, there is no call site to call anything from, and collapsing them would delete what each copy pins \u2014 a shared base type, a generated set, or one exported constant each site refers to is the move instead, and sometimes the honest answer is that there is nothing to extract at all. Reported because the copies drift apart the first time only one of them is edited, which is true whichever of those they are."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/popover.tsx"},"region":{"startLine":8}}}],"partialFingerprints":{"codehealthFindingId/v1":"3d2eb8b3d24186f43a019d4147fdaf6f0a156fc92652cb36dc3193ed13ed1b75"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (29 lines \u00D7 2 locations): packages/ui/src/components/carousel.tsx:174 \u00B7 packages/ui/src/components/carousel.tsx:204 \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":"packages/ui/src/components/carousel.tsx"},"region":{"startLine":174}}}],"partialFingerprints":{"codehealthFindingId/v1":"87ec3daadf6b10cbcd9a0847efea0f0ea1d3c74da7df8deb7806abd2afacea95"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (19 lines \u00D7 2 locations): packages/koharu/components/editor/PageRail.tsx:328 \u00B7 packages/koharu/components/start/StartView.tsx:107 \u2014 the 2 copies are spread across 2 directories, so the shared home is a decision rather than an obvious spot: check first whether one of them already owns this behaviour, and otherwise put the extracted module somewhere all of the sites already reach rather than making one of them depend on another."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/PageRail.tsx"},"region":{"startLine":328}}}],"partialFingerprints":{"codehealthFindingId/v1":"b3b5dc9ea36d0b7b34b6fa78a712a83998d6ee61b6e1cb4bfe3947b84190b33e"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (17 lines \u00D7 2 locations): packages/ui/src/components/context-menu.tsx:27 \u00B7 packages/ui/src/components/hover-card.tsx:13 \u2014 the 2 copies are spread across 2 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/context-menu.tsx"},"region":{"startLine":27}}}],"partialFingerprints":{"codehealthFindingId/v1":"435ed2ccb56c7cbc06711ab81b9fced743090bdc351de97bad8482c8407720cd"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (16 lines \u00D7 2 locations): packages/koharu/components/preferences/GenerationPreferences.tsx:38 \u00B7 packages/koharu/components/preferences/GenerationPreferences.tsx:54 \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":"packages/koharu/components/preferences/GenerationPreferences.tsx"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"6ba54a993bf3c8d2bf41216399ea25307655282c9e10cdaf5ad79c705c130cf5"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block with local edits (15 matched lines \u00D7 2 locations): packages/koharu/components/controls/ModelPicker.tsx:55 \u00B7 packages/koharu/components/controls/OutputPicker.tsx:103 \u2014 the two spans are one implementation copied and then locally edited \u2014 61 tokens are still identical, in the same order in both files, with only local edits between them. The copies have already begun to drift, which is this row\u0027s finding: an edit made to one and not the other changes behaviour silently. Diff the two spans first to learn what genuinely differs, then extract the shared core into one module both sites use, passing the differences in as parameters \u2014 or, if one copy exists only because the other could not be imported from its context, make one of them the single source the other is generated or re-exported from. If one copy is no longer reachable, delete it rather than letting it shadow the live one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":55}}}],"partialFingerprints":{"codehealthFindingId/v1":"5e265a4a4448c0e6d2106ce4877da348f8da48edd356289ff7e9f680bf0fbe58"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block with local edits (15 matched lines \u00D7 2 locations): packages/koharu/components/controls/ModelPicker.tsx:53 \u00B7 packages/koharu/components/editor/InferenceControl.tsx:386 \u2014 the two spans are one implementation copied and then locally edited \u2014 54 tokens are still identical, in the same order in both files, with only local edits between them. The copies have already begun to drift, which is this row\u0027s finding: an edit made to one and not the other changes behaviour silently. Diff the two spans first to learn what genuinely differs, then extract the shared core into one module both sites use, passing the differences in as parameters \u2014 or, if one copy exists only because the other could not be imported from its context, make one of them the single source the other is generated or re-exported from. If one copy is no longer reachable, delete it rather than letting it shadow the live one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":53}}}],"partialFingerprints":{"codehealthFindingId/v1":"2ac13f4cd875adb78e85607169c6a49e1e37736bf5bb456085230cb53dbd7e4d"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 3 locations): packages/ui/src/components/attachment.tsx:59 \u00B7 packages/ui/src/components/empty.tsx:37 \u00B7 packages/ui/src/components/item.tsx:94 \u2014 the 3 copies are spread across 3 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/attachment.tsx"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"48db62acf35199d44e040ae8122080614bb18123342098081e0a51be29e5b88d"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 3 locations): packages/ui/src/components/breadcrumb.tsx:36 \u00B7 packages/ui/src/components/bubble.tsx:59 \u00B7 packages/ui/src/components/button-group.tsx:35 \u2014 the 3 copies are spread across 3 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/breadcrumb.tsx"},"region":{"startLine":36}}}],"partialFingerprints":{"codehealthFindingId/v1":"4ce17089f858747be58de82b176d31090a5327e39f52f7b4d8f6ff9722738f0d"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (15 lines \u00D7 2 locations): packages/ui/src/components/button-group.tsx:25 \u00B7 packages/ui/src/components/field.tsx:72 \u2014 the 2 copies are spread across 2 files, and the SHAPE of this repetition could not be determined. It is not a run of declarations, a listing, a declaration header, a type body or a slice through a construct \u2014 and it was not measured as a run of executable statements either, so this row cannot tell you whether a function can stand where these lines are. Read the two spans before acting, because the move is opposite in the two cases. Where they are statements, the ordinary answer holds: give the shared part one home and call it from each site. Where they turn out to be declarations, a literal\u0027s entries, or the cases of an enumeration, there is no call site to call anything from, and collapsing them would delete what each copy pins \u2014 a shared base type, a generated set, or one exported constant each site refers to is the move instead, and sometimes the honest answer is that there is nothing to extract at all. Reported because the copies drift apart the first time only one of them is edited, which is true whichever of those they are."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/button-group.tsx"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"8445264d571e1ae1c19e5df561b6bdf878bb04b7466895816a1fe83f99a42c18"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2 locations): packages/koharu/components/editor/ActivityCenter.tsx:117 \u00B7 packages/koharu/components/editor/ActivityCenter.tsx:170 \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":"packages/koharu/components/editor/ActivityCenter.tsx"},"region":{"startLine":117}}}],"partialFingerprints":{"codehealthFindingId/v1":"b880a6e69ccb57c186ce7cdd731f8420b886f8cbc16bfe101af3655947597a74"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 11 locations): packages/ui/src/components/accordion.tsx:11 \u00B7 packages/ui/src/components/avatar.tsx:23 \u00B7 packages/ui/src/components/combobox.tsx:165 \u00B7 packages/ui/src/components/combobox.tsx:185 \u00B7 \u002B7 more site(s) not listed \u2014 the 11 copies are spread across 10 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/accordion.tsx"},"region":{"startLine":11}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d8dd8c3a92369009849a3b62fe97b75b7f4df42d224598b542637d84e3d11ac"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 17 locations): packages/ui/src/components/alert.tsx:37 \u00B7 packages/ui/src/components/attachment.tsx:83 \u00B7 packages/ui/src/components/avatar.tsx:73 \u00B7 packages/ui/src/components/breadcrumb.tsx:19 \u00B7 \u002B13 more site(s) not listed \u2014 the 17 copies are spread across 13 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/alert.tsx"},"region":{"startLine":37}}}],"partialFingerprints":{"codehealthFindingId/v1":"c5e337ead53f60e4c15360b168b318adf16b9925216370cce96a62715dfdfa0d"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 18 locations): packages/ui/src/components/alert.tsx:32 \u00B7 packages/ui/src/components/attachment.tsx:75 \u00B7 packages/ui/src/components/avatar.tsx:68 \u00B7 packages/ui/src/components/breadcrumb.tsx:14 \u00B7 \u002B14 more site(s) not listed \u2014 the 18 copies are spread across 13 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/alert.tsx"},"region":{"startLine":32}}}],"partialFingerprints":{"codehealthFindingId/v1":"1a9b1e3b9319b8270743506ec9ce6f73a5bfb350080212801fa5a40b63fd10b2"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (14 lines \u00D7 2 locations): packages/ui/src/components/button.tsx:43 \u00B7 packages/ui/src/components/toggle.tsx:30 \u2014 the 2 copies are spread across 2 files, and the SHAPE of this repetition could not be determined. It is not a run of declarations, a listing, a declaration header, a type body or a slice through a construct \u2014 and it was not measured as a run of executable statements either, so this row cannot tell you whether a function can stand where these lines are. Read the two spans before acting, because the move is opposite in the two cases. Where they are statements, the ordinary answer holds: give the shared part one home and call it from each site. Where they turn out to be declarations, a literal\u0027s entries, or the cases of an enumeration, there is no call site to call anything from, and collapsing them would delete what each copy pins \u2014 a shared base type, a generated set, or one exported constant each site refers to is the move instead, and sometimes the honest answer is that there is nothing to extract at all. Reported because the copies drift apart the first time only one of them is edited, which is true whichever of those they are."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/button.tsx"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"f5bf59b7da3d818b7210ed49d37fed5696e65c5d50e0d07eee6060e53c6e8a32"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (13 lines \u00D7 2 locations): packages/koharu/components/editor/ActivityCenter.tsx:90 \u00B7 packages/koharu/components/editor/ActivityCenter.tsx:183 \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":"packages/koharu/components/editor/ActivityCenter.tsx"},"region":{"startLine":90}}}],"partialFingerprints":{"codehealthFindingId/v1":"2172557dba865c9063513fec79904ea6a8e7ae0f451b59a07aad7e8658e98d5f"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 3 locations): packages/koharu/components/editor/AgentPanel.tsx:157 \u00B7 packages/koharu/components/editor/Inspector.tsx:297 \u00B7 packages/koharu/components/preferences/SettingsPage.tsx:288 \u2014 the 3 copies are spread across 2 directories, so the shared home is a decision rather than an obvious spot: check first whether one of them already owns this behaviour, and otherwise put the extracted module somewhere all of the sites already reach rather than making one of them depend on another. There is one copy in each of 3 separate files rather than several in one place, so check first whether these are sibling modules that each declare their own version of one shape \u2014 one per entity, per provider, per language. Where they are, no single extracted module collapses them: each module still has to be written, and what recurs is the TEMPLATE. The moves that do collapse a template are to generate these modules from the set they enumerate, or to replace the repeated construction with one factory each site calls with its own values \u2014 and where the set is the point, each module pinning one distinct thing, the repetition IS the enumeration and there is nothing to delete."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":157}}}],"partialFingerprints":{"codehealthFindingId/v1":"9a8238dbb18ed2d58f2de44c1118cbccfc34b7f26669032afdc3e597aa0849a9"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (10 lines \u00D7 2 locations): packages/ui/src/components/item.tsx:61 \u00B7 packages/ui/src/components/marker.tsx:23 \u2014 the 2 copies are spread across 2 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/item.tsx"},"region":{"startLine":61}}}],"partialFingerprints":{"codehealthFindingId/v1":"19342610eedf0f72bbd772c0b5ed1e48bb8791ff40460ac3620c25b9622d3b70"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2 locations): packages/koharu/components/editor/CanvasWorkspace.tsx:814 \u00B7 packages/koharu/components/editor/Inspector.tsx:1032 \u2014 the 2 copies sit in sibling files in one directory, so check first whether one of them (or an existing module there) already owns this behaviour and the others should call it; otherwise extract it into one module in that directory and have each site call it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":814}}}],"partialFingerprints":{"codehealthFindingId/v1":"7600852de55ce2751db07b1f6af711f7ef16d61feb61bffc4b060c8187d95d53"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (9 lines \u00D7 2 locations): packages/koharu/components/preferences/GenerationPreferences.tsx:46 \u00B7 packages/koharu/components/preferences/PipelinePreferences.tsx:122 \u2014 the 2 copies sit in sibling files in one directory, so check first whether one of them (or an existing module there) already owns this behaviour and the others should call it; otherwise extract it into one module in that directory and have each site call it."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/GenerationPreferences.tsx"},"region":{"startLine":46}}}],"partialFingerprints":{"codehealthFindingId/v1":"241ae42824c71ff7a057402578327913a344f0210877b3df41d3415c68cdb5cf"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (8 lines \u00D7 3 locations): packages/ui/src/components/alert-dialog.tsx:4 \u00B7 packages/ui/src/components/dialog.tsx:6 \u00B7 packages/ui/src/components/sheet.tsx:6 \u2014 the 3 copies are spread across 3 files, and the SHAPE of this repetition could not be determined. It is not a run of declarations, a listing, a declaration header, a type body or a slice through a construct \u2014 and it was not measured as a run of executable statements either, so this row cannot tell you whether a function can stand where these lines are. Read the two spans before acting, because the move is opposite in the two cases. Where they are statements, the ordinary answer holds: give the shared part one home and call it from each site. Where they turn out to be declarations, a literal\u0027s entries, or the cases of an enumeration, there is no call site to call anything from, and collapsing them would delete what each copy pins \u2014 a shared base type, a generated set, or one exported constant each site refers to is the move instead, and sometimes the honest answer is that there is nothing to extract at all. Reported because the copies drift apart the first time only one of them is edited, which is true whichever of those they are."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/alert-dialog.tsx"},"region":{"startLine":4}}}],"partialFingerprints":{"codehealthFindingId/v1":"8d52efa16d3618ebe90e80eaf5ace8a91a48d3d5bd22115f4a6bd11c9c09ec6b"}},{"ruleId":"R10","level":"warning","message":{"text":"Duplicated block (6 lines \u00D7 2 locations): packages/bridge/src/canvas.ts:258 \u00B7 packages/koharu/components/editor/useCanvas.ts:173 \u2014 the 2 copies are spread across 2 files, and the CITED SPAN is not a self-contained block \u2014 it runs from inside one construct into the next (the tail of a branch plus the head of the following one, a run of switch arms, the end of a declaration plus the list that follows it) rather than covering a whole unit. So do not lift these lines literally: no call can be substituted for a half-open construct. Extract the enclosing repeated UNIT instead \u2014 the whole function, component or branch these lines sit in \u2014 and where the repetition IS the construct (a run of switch arms, a stack of near-identical declarations) replace it with one table or registry looked up by key rather than a helper each arm calls. The copies still drift apart the first time only one of them is edited, which is why this is reported."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/bridge/src/canvas.ts"},"region":{"startLine":258}}}],"partialFingerprints":{"codehealthFindingId/v1":"7bd369ffaa3b9aee51117d65fe3c8b958c90ae59804b62667e2a4c00b995a558"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function (anonymous) (cyclomatic 34, cognitive 41): (anonymous) has cyclomatic complexity 34 and cognitive complexity 41; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":542}}}],"partialFingerprints":{"codehealthFindingId/v1":"d2a87124b7caac01ac0dbc9a467684a66a779dd71c472794e78dbb428a6d32ab"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function TypeInspector (cyclomatic 27, cognitive 26): TypeInspector has cyclomatic complexity 27 and cognitive complexity 26; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Inspector.tsx"},"region":{"startLine":131}}}],"partialFingerprints":{"codehealthFindingId/v1":"4d1aa3f907e775332f2641e2cb92c5716ac385cc87a5349d27ddd79b6ba7b0e3"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function layerFrame (cyclomatic 21, cognitive 23): layerFrame has cyclomatic complexity 21 and cognitive complexity 23; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/lib/geometry.ts"},"region":{"startLine":65}}}],"partialFingerprints":{"codehealthFindingId/v1":"53ec9337fa0b199ae1f39c7dd2d7c1df47fad4e09ef91941f543b971b6ffb81f"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function SettingsPage (cyclomatic 21, cognitive 19): SettingsPage has cyclomatic complexity 21 and cognitive complexity 19; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/SettingsPage.tsx"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"ba45d4cc6cd688bf60a517d5e6bb70202f2c6ee07046bd7d79f1c725a4e67151"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function RuntimeSelector (cyclomatic 20, cognitive 17): RuntimeSelector has cyclomatic complexity 20 and cognitive complexity 17; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/InferenceControl.tsx"},"region":{"startLine":86}}}],"partialFingerprints":{"codehealthFindingId/v1":"91ac7c0b7947c24e4e9963f715988be66722b50ba34a95714855e5d0567b60ba"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function down (cyclomatic 19, cognitive 20): down has cyclomatic complexity 19 and cognitive complexity 20; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":320}}}],"partialFingerprints":{"codehealthFindingId/v1":"7622bb87ebd711b3ea01fe8ffdda8eb4acf9022b6747e53a1a2b0f0bbc59b597"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function (anonymous) (cyclomatic 18, cognitive 13): (anonymous) has cyclomatic complexity 18 and cognitive complexity 13; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/chart.tsx"},"region":{"startLine":202}}}],"partialFingerprints":{"codehealthFindingId/v1":"97c868baf70cb09cc51860c6580d5263ef4b70660b5257acbc06a013269e8459"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function Updater (cyclomatic 17, cognitive 16): Updater has cyclomatic complexity 17 and cognitive complexity 16; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/app/Updater.tsx"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"fc9948994e7e23b8c6489197434beb35da3d8e0dca1dd205722fb4aaeee69fc5"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function (anonymous) (cyclomatic 17, cognitive 16): (anonymous) has cyclomatic complexity 17 and cognitive complexity 16; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":112}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c93498f5351ad81b06670813cfbb1de8cf36bada1a8b09562c9b87d095620c1"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function TitleBar (cyclomatic 16, cognitive 14): TitleBar has cyclomatic complexity 16 and cognitive complexity 14; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/app/TitleBar.tsx"},"region":{"startLine":40}}}],"partialFingerprints":{"codehealthFindingId/v1":"b3b258ef43fc910f80e25bd392a46cbd405bd8d2079d770a39f227ccf14a260b"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function ModelOptions (cyclomatic 16, cognitive 7): ModelOptions has cyclomatic complexity 16 and cognitive complexity 7; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/PipelinePreferences.tsx"},"region":{"startLine":104}}}],"partialFingerprints":{"codehealthFindingId/v1":"ae8c2b9c22fc5271803c1ffcbfa3758a8625eb800e85ae9ff68aacbdf8377df1"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function finishGesture (cyclomatic 15, cognitive 19): finishGesture has cyclomatic complexity 15 and cognitive complexity 19; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":465}}}],"partialFingerprints":{"codehealthFindingId/v1":"55c046e068e11201c0221c0ead2527e225c1c10aa12e6bc387595f30d84fb3fd"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function moveGesture (cyclomatic 15, cognitive 14): moveGesture has cyclomatic complexity 15 and cognitive complexity 14; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":408}}}],"partialFingerprints":{"codehealthFindingId/v1":"f1206708042e6490f6699386df4876f5c99b4f39418e28815a5ff09d0357ada5"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function CanvasWorkspace (cyclomatic 15, cognitive 12): CanvasWorkspace has cyclomatic complexity 15 and cognitive complexity 12; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":83}}}],"partialFingerprints":{"codehealthFindingId/v1":"7a2960e111e180f4c52903407e544713389c5dc91b651a7ed343d87ad98c7909"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function AgentPanel (cyclomatic 14, cognitive 16): AgentPanel has cyclomatic complexity 14 and cognitive complexity 16; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":38}}}],"partialFingerprints":{"codehealthFindingId/v1":"497fe2059140fe909968f41395400a7c8a4ac6579851f8d7ee609abb5cf38191"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function replaceStage (cyclomatic 14, cognitive 14): replaceStage has cyclomatic complexity 14 and cognitive complexity 14; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/preferences/models.ts"},"region":{"startLine":59}}}],"partialFingerprints":{"codehealthFindingId/v1":"8d7ccad7d28c2aff1c6c3dc296190a2629b52db44140486f81a234899b35583a"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function ModelPicker (cyclomatic 13, cognitive 10): ModelPicker has cyclomatic complexity 13 and cognitive complexity 10; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/ModelPicker.tsx"},"region":{"startLine":18}}}],"partialFingerprints":{"codehealthFindingId/v1":"e1baf77559e4f7971e0e426f84b2810956e1ce0cabbd04af862e0129c41ea3aa"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function (anonymous) (cyclomatic 12, cognitive 18): (anonymous) has cyclomatic complexity 12 and cognitive complexity 18; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive is at or above cyclomatic here, so the branching is nested or entangled rather than laid out side by side \u2014 extracting each decision into its own named function is the change that pays. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/CanvasWorkspace.tsx"},"region":{"startLine":691}}}],"partialFingerprints":{"codehealthFindingId/v1":"1e999eef05f9ce5c32dbc5881d55117dcfe64d550da8d84e5623dd5f6c686daa"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function getPayloadConfigFromPayload (cyclomatic 12, cognitive 11): getPayloadConfigFromPayload has cyclomatic complexity 12 and cognitive complexity 11; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/ui/src/components/chart.tsx"},"region":{"startLine":330}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c262fd1ab9eed913a8ebb7df72bad56c3c917f90ba416026f0bf0df3bf5256b"}},{"ruleId":"R2","level":"warning","message":{"text":"Complex function scriptRank (cyclomatic 12, cognitive 10): scriptRank has cyclomatic complexity 12 and cognitive complexity 10; this row is raised above a cyclomatic bar of 10. The two numbers answer different questions and the gap between them is what decides whether to act: cyclomatic counts the independent arms through the body, cognitive counts what it costs to hold them in your head, so nesting and mixed boolean chains raise it while a flat run of independent arms does not. Cognitive sits below cyclomatic here, so much of the count is breadth \u2014 arms side by side rather than stacked \u2014 and splitting per arm would leave a function per arm; group the work between the checks into named steps instead. Measured by this repository\u0027s own parse of the file, so a body assembled at runtime, or generated, is counted as written rather than as it executes."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/controls/FontPicker.tsx"},"region":{"startLine":510}}}],"partialFingerprints":{"codehealthFindingId/v1":"017d4b4a624ec0719db4e6a24c3ec8a5d20629b34dfb5a631f2d1bee6b6d029f"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/AgentPanel.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"bf025cf0b8999a74910ba7ac904dcb1e72c16db9447014022b4b20253d13b4d9"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/kinde.ts"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"2441a9d217911e717cbd00bb62cc9036998345698d3e358fa25c7709c1affa53"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/release.ts"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f7bc8387f17b73b0c7765f89a02e945bce24d54606e178e075e29958fe30762f"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/Editor.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"33ee179d82ccc0c7acfb26094f8c35363521a7136b028e143a08db49a2036550"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/app/KoharuApp.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"5b135de923027199040963204f1525f00a23af11c8bbabdb7ef869b038209ffb"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/components/editor/RightSidebar.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"713f8f0e6b6f53c88178095286ebb739b76b97dc1fc63824c91c4bf719b67b8d"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"scripts/download_bluearchive_comics.ts"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"6fccc2561f38a44f5f1457bb66d5de5b3e5e27da3a1465b990a563e8bae90bdf"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/layout.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"c0cb7a5f00aab15d795ee528b80c2e7e11e5bc8f1829048207efb5b95dad6466"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/global-error.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"fe43d5e164a86b9cb043f4cdc51aa1e1795779e11184e33aff0f26ac269333d0"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/(app)/page.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"d258e1108ef145e8ac37eaf87ec01a6f2a1f62ba094e8c7908761623826294a4"}},{"ruleId":"R4","level":"warning","message":{"text":"No test reaches this file: No test imports this module directly or transitively. Import reachability cannot see a test that executes a file by path instead of importing it, nor one that drives it through a running browser by navigating to a URL \u2014 if neither does, no test reaches this one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/app/(app)/layout.tsx"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"48906ed3f24de258d00c257e8f2894c6117956db4cffd085358c379d76344753"}},{"ruleId":"R7","level":"note","message":{"text":"Unused export \u0027dispatch\u0027: Nothing imports this binding \u2014 it is safe to review for removal."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"packages/koharu/lib/backend.ts"},"region":{"startLine":31}}}],"partialFingerprints":{"codehealthFindingId/v1":"103e9d4c434e8b94ab610a016da0a06d8705a6413e821564faaee74a271202ab"}},{"ruleId":"X7","level":"note","message":{"text":"Silent fallback default on Err: \u0060find_key\u0060 turns every \u0060Err\u0060 into \u0060-1\u0060 \u2014 a failure becomes a plausible value and a silent behavior change with no trail. Log the error or propagate it with \u0060?\u0060. If that constant is the correct answer rather than a stand-in for a value that could not be read, say so in a comment on the arm or in the doc comment, and the row stops firing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-llama/src/gguf/mod.rs"},"region":{"startLine":43}}}],"partialFingerprints":{"codehealthFindingId/v1":"8d98619055a413fac38a3e3f28a9f9b2601eb0342723f47ed814946c2d3de2d8"}},{"ruleId":"X7","level":"note","message":{"text":"Silent fallback default on Err: \u0060sample_drm\u0060 turns every \u0060Err\u0060 into \u0060Vec::new()\u0060 \u2014 a failure becomes a plausible value and a silent behavior change with no trail. Log the error or propagate it with \u0060?\u0060. If that constant is the correct answer rather than a stand-in for a value that could not be read, say so in a comment on the arm or in the doc comment, and the row stops firing."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"crates/koharu-pipeline/src/resources/linux.rs"},"region":{"startLine":78}}}],"partialFingerprints":{"codehealthFindingId/v1":"c1ddf7795fa27b378190c84a18b3f8b04e133e0a56a83aba15b7a0af648500a0"}}],"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-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-310","guid":"97174724-2e33-8455-b7cd-5c3258d1cb8f","name":"CWE-310","shortDescription":{"text":"CWE-310"},"helpUri":"https://cwe.mitre.org/data/definitions/310.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-552","guid":"3492436b-eca2-9c54-9ba3-5dade427c903","name":"CWE-552","shortDescription":{"text":"CWE-552"},"helpUri":"https://cwe.mitre.org/data/definitions/552.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":64,"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."]}]}