{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"codehealth","informationUri":"https://codehealth.canine.dev","rules":[{"id":"D1","name":"Cyclomatic Complexity","shortDescription":{"text":"Cyclomatic Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D1"},{"id":"D2","name":"Cognitive Complexity","shortDescription":{"text":"Cognitive Complexity"},"helpUri":"https://codehealth.canine.dev/dimensions/D2"},{"id":"D3","name":"God Classes","shortDescription":{"text":"God Classes"},"helpUri":"https://codehealth.canine.dev/dimensions/D3"},{"id":"D4","name":"Code Duplication","shortDescription":{"text":"Code Duplication"},"helpUri":"https://codehealth.canine.dev/dimensions/D4"},{"id":"D6","name":"Cohesion (LCOM4)","shortDescription":{"text":"Cohesion (LCOM4)"},"helpUri":"https://codehealth.canine.dev/dimensions/D6"},{"id":"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":"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"]},{"target":{"id":"A07:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"],"owasp":"A07:2025"}},{"id":"D15","name":"Churn \u00D7 Complexity Hotspots","shortDescription":{"text":"Churn \u00D7 Complexity Hotspots"},"helpUri":"https://codehealth.canine.dev/dimensions/D15"},{"id":"D16","name":"Bus Factor","shortDescription":{"text":"Bus Factor"},"helpUri":"https://codehealth.canine.dev/dimensions/D16"},{"id":"D17","name":"Explicit Debt","shortDescription":{"text":"Explicit Debt"},"helpUri":"https://codehealth.canine.dev/dimensions/D17"},{"id":"D19","name":"Documentation Quality","shortDescription":{"text":"Documentation Quality"},"helpUri":"https://codehealth.canine.dev/dimensions/D19"},{"id":"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"]},{"target":{"id":"A07:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-798","CWE-259"],"owasp":"A07:2025"}},{"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"]},{"target":{"id":"A05:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-79","CWE-89","CWE-78","CWE-94","CWE-77"],"owasp":"A05:2025"}},{"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"]},{"target":{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},"kinds":["relevant"]}],"properties":{"cwe":["CWE-1357","CWE-494"],"owasp":"A03:2025"}},{"id":"D44","name":"Platform End-of-Life","shortDescription":{"text":"Platform End-of-Life"},"helpUri":"https://codehealth.canine.dev/dimensions/D44"},{"id":"AX10","name":"Code composition","shortDescription":{"text":"Code composition"},"helpUri":"https://codehealth.canine.dev/dimensions/AX10"},{"id":"AX3","name":"Project dependency cycles","shortDescription":{"text":"Project dependency cycles"},"helpUri":"https://codehealth.canine.dev/dimensions/AX3"},{"id":"AX4","name":"Dependency direction","shortDescription":{"text":"Dependency direction"},"helpUri":"https://codehealth.canine.dev/dimensions/AX4"},{"id":"AXB1","name":"Runtime evidence locked \u2014 no reproducible boot","shortDescription":{"text":"Runtime evidence locked \u2014 no reproducible boot"},"helpUri":"https://codehealth.canine.dev/dimensions/AXB1"},{"id":"AXB2","name":"Runtime readiness","shortDescription":{"text":"Runtime readiness"},"helpUri":"https://codehealth.canine.dev/dimensions/AXB2"},{"id":"ED5","name":"Idempotency","shortDescription":{"text":"Idempotency"},"helpUri":"https://codehealth.canine.dev/dimensions/ED5"},{"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":"P3","name":"Security \u0026 performance tooling","shortDescription":{"text":"Security \u0026 performance tooling"},"helpUri":"https://codehealth.canine.dev/dimensions/P3"},{"id":"P6","name":"Release Hygiene","shortDescription":{"text":"Release Hygiene"},"helpUri":"https://codehealth.canine.dev/dimensions/P6"},{"id":"X10","name":"Duplicated predicate","shortDescription":{"text":"Duplicated predicate"},"helpUri":"https://codehealth.canine.dev/dimensions/X10"},{"id":"X14","name":"Bypassable address classification","shortDescription":{"text":"Bypassable address classification"},"helpUri":"https://codehealth.canine.dev/dimensions/X14"},{"id":"X15","name":"Unvalidated length from an untrusted reader","shortDescription":{"text":"Unvalidated length from an untrusted reader"},"helpUri":"https://codehealth.canine.dev/dimensions/X15"},{"id":"X16","name":"Unfloored truncation loop","shortDescription":{"text":"Unfloored truncation loop"},"helpUri":"https://codehealth.canine.dev/dimensions/X16"},{"id":"X18","name":"Disposal-pattern correctness","shortDescription":{"text":"Disposal-pattern correctness"},"helpUri":"https://codehealth.canine.dev/dimensions/X18"},{"id":"X19","name":"Unrestored process-global state","shortDescription":{"text":"Unrestored process-global state"},"helpUri":"https://codehealth.canine.dev/dimensions/X19"},{"id":"X20","name":"Mistyped argument guard","shortDescription":{"text":"Mistyped argument guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X20"},{"id":"X21","name":"Side-effecting pattern guard","shortDescription":{"text":"Side-effecting pattern guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X21"},{"id":"X22","name":"Contradicted release guard","shortDescription":{"text":"Contradicted release guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X22"},{"id":"X23","name":"Unguarded diagnostic materialisation","shortDescription":{"text":"Unguarded diagnostic materialisation"},"helpUri":"https://codehealth.canine.dev/dimensions/X23"},{"id":"X25","name":"Inert configuration knob","shortDescription":{"text":"Inert configuration knob"},"helpUri":"https://codehealth.canine.dev/dimensions/X25"},{"id":"X26","name":"Unsynchronised callback handoff","shortDescription":{"text":"Unsynchronised callback handoff"},"helpUri":"https://codehealth.canine.dev/dimensions/X26"},{"id":"X28","name":"Index access outside its own emptiness guard","shortDescription":{"text":"Index access outside its own emptiness guard"},"helpUri":"https://codehealth.canine.dev/dimensions/X28"},{"id":"X3","name":"Exception handling","shortDescription":{"text":"Exception handling"},"helpUri":"https://codehealth.canine.dev/dimensions/X3"},{"id":"X30","name":"Support guard that admits what it rejects","shortDescription":{"text":"Support guard that admits what it rejects"},"helpUri":"https://codehealth.canine.dev/dimensions/X30"},{"id":"X32","name":"Type resolved by simple name across every loaded assembly","shortDescription":{"text":"Type resolved by simple name across every loaded assembly"},"helpUri":"https://codehealth.canine.dev/dimensions/X32"},{"id":"X5","name":"Nullable reference types","shortDescription":{"text":"Nullable reference types"},"helpUri":"https://codehealth.canine.dev/dimensions/X5"},{"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":"D2","level":"warning","message":{"text":"HandlesConversations.loadActiveConversation (cognitive 21): HandlesConversations.loadActiveConversation has cognitive complexity 21 (threshold 15). Drivers by points: if/else 10 (17 pts), loops 2 (3 pts), boolean chains 1 (nesting depth added 8). To reduce it, split the body into named stages: move each independent step or branch into its own named function so the body reads as a short sequence of named calls rather than one long body."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Traits/HandlesConversations.php"},"region":{"startLine":170}}}],"partialFingerprints":{"codehealthFindingId/v1":"01f9e83c2fa0fac826792418e28b33fa1a8dfddb565b8edfd6234fb87d14ab08"}},{"ruleId":"D3","level":"warning","message":{"text":"TooManyMethods: BotMan: TooManyMethods \u2014 42 methods. The bar is 30 methods; this is 12 over it, 1.40\u00D7 the bar. To reduce it, group the members that share the same data into a smaller type of their own and delegate to it, so no single type carries every responsibility."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/BotMan.php"},"region":{"startLine":44}}}],"partialFingerprints":{"codehealthFindingId/v1":"bb88a6ac4241a37ab93d6308a27a988566dce33bd0fa6269ec8ea1b390879f59"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (11 lines \u00D7 2): src/Cache/RedisCache.php:121-131 | src/Storages/Drivers/RedisStorage.php:103-113 \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":"src/Cache/RedisCache.php"},"region":{"startLine":121}}}],"partialFingerprints":{"codehealthFindingId/v1":"a2bd15846ab74f79152cd78bfb16fa46f80972505921b614637123a8b6f63983"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (5 lines \u00D7 2): src/Messages/Attachments/Attachment.php:51-55 | src/Messages/Incoming/IncomingMessage.php:127-131 \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":"src/Messages/Attachments/Attachment.php"},"region":{"startLine":51}}}],"partialFingerprints":{"codehealthFindingId/v1":"43ae5a94eadcbdf55c8053af5d095b01565e1c239915978ea0a3ee317d3f5810"}},{"ruleId":"D4","level":"warning","message":{"text":"Duplicated block (7 lines \u00D7 2): src/Cache/CodeIgniterCache.php:78-84 | src/Cache/DoctrineCache.php:80-86 \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":"src/Cache/CodeIgniterCache.php"},"region":{"startLine":78}}}],"partialFingerprints":{"codehealthFindingId/v1":"78e2f23bcaeb26774c88903d7a88cbcf25248179d9110bc9b491766688db16bb"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: FakeDriver (LCOM4 6): FakeDriver\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Drivers/Tests/FakeDriver.php"},"region":{"startLine":30}}}],"partialFingerprints":{"codehealthFindingId/v1":"37ac228fb9f1a1969bcd4be2a538d11e0cf3fbe13f8d00001e10b6843780bcbc"}},{"ruleId":"D6","level":"warning","message":{"text":"Low cohesion: MiddlewareManager (LCOM4 6): MiddlewareManager\u0027s methods fall into 6 groups that share no field and call none of each other, against a bar of more than 3 for this run (LCOM4, configurable \u2014 your repository\u0027s bar is the one quoted here). Each group is a set of methods reachable from one another through shared fields or direct calls, so 6 groups means the type has that many internally-connected clusters with nothing tying them together. Types whose shape makes a high count expected \u2014 and which would otherwise dominate this list \u2014 are excluded before this row is raised, so this is a genuine split candidate rather than a metric reading. It is still a shape, not a defect: confirm the groups match responsibilities you can name before splitting."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Middleware/MiddlewareManager.php"},"region":{"startLine":15}}}],"partialFingerprints":{"codehealthFindingId/v1":"6e1869a7c9a79a62846c55d5ed5a70be0d194273edd410c901a6a4b6c194e742"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_caches_conversation_for_30_minutes: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManConversationTest.php"},"region":{"startLine":58}}}],"partialFingerprints":{"codehealthFindingId/v1":"a12f1ddac6d2a5c3af8db756fdd09ac9ad0a0c5cbdc1b1759ccbd5d094f17273"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_caches_conversation_for_custom_amount_of_minutes: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManConversationTest.php"},"region":{"startLine":74}}}],"partialFingerprints":{"codehealthFindingId/v1":"210d33224416d86e90225eb83e20de7ee8a69fb9cb62d35d1a9a15d859c2ab97"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_uses_conversation_defined_cache_time: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManConversationTest.php"},"region":{"startLine":99}}}],"partialFingerprints":{"codehealthFindingId/v1":"7d20cda29a0d84307441ebe7498507c6f01fe2e0da355513a58c0b14ec09fceb"}},{"ruleId":"D10","level":"error","message":{"text":"No assertions (empty test): it_can_start_conversations: Test method has an empty body \u2014 it asserts nothing and exercises no code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":817}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca90f096a078ccb487874bf814f42bfe890b8eacc7b90c9090aca963f62b205f"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_originate_messages_with_given_driver: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1506}}}],"partialFingerprints":{"codehealthFindingId/v1":"3f0c30a302c97e529196efdf3313bd4c48fff41d45c3d16e78be8ee32ba0048f"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_originate_messages_to_multiple_users: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1533}}}],"partialFingerprints":{"codehealthFindingId/v1":"b674d385a34cd1855e02db88b11e73487c62d9f0d362a0049c6e538008bf3252"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_originate_messages_with_additional_parameters: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1564}}}],"partialFingerprints":{"codehealthFindingId/v1":"1869d80251aaf3dd1166e7ca5c18fbccc1665f7118b36608e1d212149d9a5d07"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_originate_messages_with_configured_driver: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1595}}}],"partialFingerprints":{"codehealthFindingId/v1":"ce5718ed621880cd6c5e88eb25a0909fe0eb6511542a594adfe29e8b4f94e4a0"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_use_custom_drivers: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1626}}}],"partialFingerprints":{"codehealthFindingId/v1":"91413476d2aca3d5e26facc4355ae036035ccac5076516b97ac92f2234458103"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_passes_unknown_methods_to_the_driver: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1646}}}],"partialFingerprints":{"codehealthFindingId/v1":"9d40bdc8e66e1e11d75004590ad8e676084382afeb13473ec8aff9dce4d1c592"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_repeat_a_question: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1679}}}],"partialFingerprints":{"codehealthFindingId/v1":"a3532660cebe32ab17961025a2601c3c68d1007485c27206af7bc774f0b48052"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_repeat_a_modified_question: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/BotManTest.php"},"region":{"startLine":1734}}}],"partialFingerprints":{"codehealthFindingId/v1":"7ece5ed37997a102682164bbf56827ded1147fa7fac096daf46e4a7135290746"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/CodeIgniterCacheTest.php"},"region":{"startLine":93}}}],"partialFingerprints":{"codehealthFindingId/v1":"cfb4fa7c76d449b8113dde7d6ea1c52bae1f9be5539d3cc3c378bd1cb8ff8eec"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put_with_datetime: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/CodeIgniterCacheTest.php"},"region":{"startLine":107}}}],"partialFingerprints":{"codehealthFindingId/v1":"4758b18b3dc18f514b6121dd7ebda8153902b4fea0d03aa4fe5d3463a2dfc749"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/DoctrineCacheTest.php"},"region":{"startLine":82}}}],"partialFingerprints":{"codehealthFindingId/v1":"b892cffb38dec9f68536aa70767455e5218b3c72f68901d498d67e3e09af9e9a"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put_with_datetime: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/DoctrineCacheTest.php"},"region":{"startLine":92}}}],"partialFingerprints":{"codehealthFindingId/v1":"9bf5942d9b52b609abb875b3f80428990a313a5145cd9d6839fee00b4f75eab9"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/Psr6CacheTest.php"},"region":{"startLine":109}}}],"partialFingerprints":{"codehealthFindingId/v1":"1cc4b0c692ad0de877df74dbfd895356b0fc8a4ec5775538b7265a0bbd76bf25"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put_with_datetime: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/Psr6CacheTest.php"},"region":{"startLine":124}}}],"partialFingerprints":{"codehealthFindingId/v1":"8a90d6fb11318ffeb69944816c4529ce9b0482dd3fe9122da65eb36a80a32b53"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: invalid_auth: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/RedisCache/SecureTest.php"},"region":{"startLine":47}}}],"partialFingerprints":{"codehealthFindingId/v1":"7a7a3011b6c8e10c9b684e4d0a3ac5afd69c3161166f6238aaa31a558c09b1fd"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/SymfonyCacheTest.php"},"region":{"startLine":110}}}],"partialFingerprints":{"codehealthFindingId/v1":"fa963b687c80c346f73ae38bfb57c24436faa2997b91f9428a768b251f07e21e"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: put_with_datetime: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Cache/SymfonyCacheTest.php"},"region":{"startLine":125}}}],"partialFingerprints":{"codehealthFindingId/v1":"ee4f909d9c8509e357e76cb4637e024e4033bdfbdbb27176334720c8925fd685"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_can_reply: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/ConversationTest.php"},"region":{"startLine":25}}}],"partialFingerprints":{"codehealthFindingId/v1":"550d148110242c2d70e9408e447581a3e072dde466f25896eba36f1e43925713"}},{"ruleId":"D10","level":"error","message":{"text":"No assertions (empty test): it_can_ask_questions: Test method has an empty body \u2014 it asserts nothing and exercises no code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/ConversationTest.php"},"region":{"startLine":39}}}],"partialFingerprints":{"codehealthFindingId/v1":"4e9fdb6e3d741aa3770247456bcc11460aa2a9453ba9d67d40f3265dae3edac7"}},{"ruleId":"D10","level":"error","message":{"text":"No assertions (empty test): it_can_ask_question_with_multiple_callbacks: Test method has an empty body \u2014 it asserts nothing and exercises no code."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/ConversationTest.php"},"region":{"startLine":60}}}],"partialFingerprints":{"codehealthFindingId/v1":"cb3227c1ab2b659e792d9bc770fc24a8df5cb388b0bde24fe0d3cd7ffd5edda0"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_only_calls_service_once_per_listen: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Middleware/ApiAiTest.php"},"region":{"startLine":158}}}],"partialFingerprints":{"codehealthFindingId/v1":"bd184d7d3394d073601a620c7ae6752ac3e98287a4bac27b0d2956becfeccf7e"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: it_only_calls_service_once_per_listen: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Middleware/WitTest.php"},"region":{"startLine":151}}}],"partialFingerprints":{"codehealthFindingId/v1":"af16d1264d43483c23917e1518a13b5dde702dbefdbfa203644cdcadbb30fab7"}},{"ruleId":"D10","level":"warning","message":{"text":"No assertions: invalid_auth: This method\u0027s body runs code, and no assertion call was recognised in it. Recognised by name: Assert*, *Should*/ShouldBe*, Verify, Expect, Throws, Record, Received/DidNotReceive, MustHaveHappened/MustNotHaveHappened, EnsureSuccessStatusCode and *AndEnsure* \u2014 so verification routed through a helper of your own naming, through a base-class or callback object whose members hold the assertions, or through a harness that fails by throwing under some other name, is not visible to this check and is not counted here. Read it as \u0027no assertion this check knows how to see\u0027, and if that is right, add one."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"tests/Storages/RedisStorage/SecureTest.php"},"region":{"startLine":49}}}],"partialFingerprints":{"codehealthFindingId/v1":"8811c199298809750097e018bcd01ef20b5ed8696cb3f229d5e9234cb40f187c"}},{"ruleId":"D16","level":"note","message":{"text":"Off-boarding risk: anonymized user #1: If anonymized user #1 becomes unavailable, 3 significant file(s) lose their only recent owner: src/Drivers/DriverManager.php, src/BotManFactory.php, src/Middleware/MiddlewareManager.php. Pair on, review, or document these before any departure."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"9a534eaa6ed8e4ca5b3f9f1b8b0a1c0b3c122df552d096d1e1c75420331a1e49"}},{"ruleId":"D17","level":"warning","message":{"text":"TodoComment: // TODO: Needs more work \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":"src/Traits/HandlesConversations.php"},"region":{"startLine":314}}}],"partialFingerprints":{"codehealthFindingId/v1":"db3b7a61382032034a5a97a7f8aac5d3dd81df49469bd243664791be97cd89b1"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no installation or build instructions: There are no installation, build, or setup instructions anywhere in the visible text. Add a short install/requirement line and any build steps needed to get started."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"README.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1fa6bfcd5c6ae231d1b8ee99bced50b832d1f61cde9da6cbb533467b879a57db"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no usage examples: The visible content shows only an example of how to call $botman-\u003Ehears, but no explanation of what the code does or how to run it. Document each major API function and add a \u0027How to run this\u0027 section with a runnable example."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"README.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1355eb4e127a4bc9236772ce1c46f09510aa9286e7a06d1e47f308229df19049"}},{"ruleId":"D19","level":"note","message":{"text":"Documentation: no contributor guidance: The visible text gives no guidance for contributors, which is required by the repository\u0027s own documentation. Add a short Contributing section covering PR/merge process and any needed setup."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"README.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"620f29787918fa3406ae55cb63eefa1b50410845435c4e47bda852816aaea084"}},{"ruleId":"D22","level":"warning","message":{"text":"Inconsistent naming for message retrieval operations. \u0060getMessages\u0060 (plural) and \u0060getBotMessages\u0060 (plural) suggest collections, while \u0060getMessage\u0060 (singular) and \u0060getOutgoingMessage\u0060 (singular) suggest single items. However, \u0060getOutgoingMessage\u0060 is singular despite outgoing messages often being a stream or batch, and \u0060getBotMessages\u0060 is ambiguous compared to \u0060getMessages\u0060. It is unclear if \u0060getMessages\u0060 returns all messages or only incoming ones, and if \u0060getBotMessages\u0060 is a subset or a different type.: Standardize on \u0060getIncomingMessages()\u0060 and \u0060getOutgoingMessages()\u0060 for collections. If single item access is needed, use \u0060getCurrentMessage()\u0060 or \u0060getLastMessage()\u0060 with clear context. Remove \u0060getBotMessages\u0060 if it duplicates \u0060getMessages\u0060 or rename to \u0060getBotSentMessages()\u0060 for clarity. (signatures: BotMan.getMessages() | BotMan.getBotMessages() | BotMan.getMessage() | BotMan.getOutgoingMessage())"},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"81bfb562e1f98a7563ef37500a31692a93f69135004551cd15b1095b60ae381a"}},{"ruleId":"D22","level":"warning","message":{"text":"Inconsistent verb usage for event registration. \u0060hears\u0060 is used for text patterns, while \u0060receives*\u0060 is used for specific media types. \u0060on\u0060 is a generic catch-all. This creates a fragmented API where the verb changes based on the payload type rather than a unified \u0060on(event, callback)\u0060 or \u0060hears(pattern, callback)\u0060 pattern. \u0060receives*\u0060 methods are essentially specialized \u0060on\u0060 calls but with different naming conventions.: Unify under a single \u0060on(eventType, callback)\u0060 or \u0060hears(pattern, callback)\u0060 interface. If specific helpers are kept, ensure they follow the same verb structure (e.g., all \u0060on*\u0060 or all \u0060receives*\u0060). Currently, \u0060hears\u0060 implies listening to speech/text, while \u0060receives\u0060 implies receiving objects, which is a semantic inconsistency in the domain model. (signatures: BotMan.hears(pattern, callback, in) | BotMan.on(names, callback) | BotMan.receivesImages(callback) | BotMan.receivesVideos(callback) | BotMan.receivesAudio(callback) | BotMan.receivesLocation(callback) | BotMan.receivesContact(callback) | BotMan.receivesFiles(callback))"},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"62bf814e862d87705ba3132ed418c2570ea25895bd2c4c70e1c548c9fed6ba21"}},{"ruleId":"D22","level":"warning","message":{"text":"Overlapping functionality for sending messages. \u0060say\u0060 and \u0060reply\u0060 both send text messages but with different signatures (recipients/driver vs. implicit context). \u0060sendPayload\u0060 is a low-level method that likely does the same thing as \u0060say\u0060 but accepts a raw payload. This forces users to choose between high-level convenience methods and low-level implementation details without clear separation of concerns.: Consolidate message sending into a single primary method like \u0060send(message, options)\u0060 or \u0060reply(message)\u0060. Deprecate \u0060sendPayload\u0060 in favor of using the underlying driver directly if advanced control is needed, or ensure \u0060sendPayload\u0060 is strictly for non-text binary data if \u0060say\u0060 is text-only. (signatures: BotMan.say(message, recipients, driver, additionalParameters) | BotMan.reply(message, additionalParameters) | BotMan.sendPayload(payload))"},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"b4383ee8b7e6f26ac2528198f0024eac2dbad08af3abf25618d5a3d720aeace3"}},{"ruleId":"D22","level":"warning","message":{"text":"Redundant method pairs. \u0060stopsConversation\u0060 and \u0060shouldStopConversation\u0060 appear to serve the same intent (controlling conversation flow), as do \u0060skipsConversation\u0060 and \u0060shouldSkipConversation\u0060. Having both getter/setter-like pairs and direct action methods is confusing. It is unclear if one is for checking state and the other for setting it, or if they are aliases.: Remove the redundant pairs. Use \u0060shouldStopConversation()\u0060 and \u0060shouldSkipConversation()\u0060 as the single source of truth for determining flow control, or use \u0060stopConversation()\u0060 and \u0060skipConversation()\u0060 as imperative actions. Do not expose both forms. (signatures: Command.stopsConversation() | Command.shouldStopConversation() | Command.skipsConversation() | Command.shouldSkipConversation())"},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"290226b785b54fb776365831b204853f83f1610dabefa39aac9f574d817392fb"}},{"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":"6c2bb6e48ba198d464ab91a1bff4548c621984de3f57f09feeb3a586b65963ef"},"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"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}}]},{"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":"dcec9573fededa9369d7813dddcc301f4be53fe41f0f0b994d443aa6de24b14f"},"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"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}}]},{"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":"8a09be642eddaeccc3b7747d20ca12b4e5e143682e88c838d9eb5c0b3f5ea630"},"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"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}}]},{"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":"ad802125a4a401fcc72dd926f520e28347e5b710636c8418e0301f554098ab3f"},"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"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}}]},{"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":"930204f24cb2ec2de9252a118050380ad4615c6d78ce74000ccdb6a47eabf40f"},"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"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}}]},{"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":"5c69b484ec756d4916e14a04ee03cf6c5312e79e0e01be5125617bdb0aab9252"},"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"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}}]},{"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":"82dfdd18596fc8276e375615a034a1b1a174ddafbf2639c9e64ba09cdcdbaebd"},"taxa":[{"id":"CWE-1104","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-1329","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},{"id":"CICD-SEC-8","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"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":"eacc45e3ae65307eb2a833939abb69960aa8b865a265e9d0f3818d40a4275f79"},"taxa":[{"id":"CWE-1104","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"CWE-1329","toolComponent":{"name":"CWE","guid":"c3a2b1d0-7f3e-4b2a-9c1d-5e6f7a8b9c0d"}},{"id":"A03:2025","toolComponent":{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61"}},{"id":"CICD-SEC-8","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"ruleId":"D34","level":"note","message":{"text":"Dormant codebase: 21 of 21 significant files have no living knowledge \u2014 the codebase as a whole is dormant, not 21 separate risks. Counted over 21 of the 71 production source files in this repository: the rest are under the ~2,400-byte size floor this dimension measures over. Re-engage owners or document before change."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"4c69f1e54b7dd6fe9029dd02df6ba8f8145b789f2f18dbdaa086c40ded49dba6"}},{"ruleId":"D34","level":"note","message":{"text":"Most significant orphaned file: One of the orphaned files carrying the most lost knowledge \u2014 ranked by size weighted by the file\u0027s role in the codebase, the same weighting behind the score above, so core code outranks equally large plumbing. A reasonable place to start a read-through before the aggregate risk above bites."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/BotMan.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"6278b27694ab52e16b3ea7ca28eccd2424e9667b477207e1e5cca24add84e972"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Traits/HandlesConversations.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"7d77402b19df647e811c1e28f4f9b43f73586c145041bc35be1f024abc26a315"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Messages/Conversations/Conversation.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"419bb02cda9e5c79a513e46f3ff42a2f2d2464da1cdda3e098bd1c5467f95c64"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Middleware/ApiAi.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1fbe34fcc8ac6b934271264adbfca01e587dc7501e6d99ff1491a85d50a4bf74"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Drivers/DriverManager.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"8ab9cb3142b612baa57de5542ba3424d6e9d56c2f7c8a7edb0cbe9b9e334d2f6"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Messages/Incoming/IncomingMessage.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"93ac006a7baebc37f53f5acf73e7692e87c85026ee1d8a166f6877b3bda38838"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Commands/Command.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"4b44f52dfe613b51bdf0215d055be6e7fd7abe90a82619b014b780a99396c877"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/BotManFactory.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"1c1d3fff872db5652304577165124481b31e33fc94988217fd2615952faadc67"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Http/Curl.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f7b6661bd7c34c04c46a284828f8130903cb48ed6dfdb59c9ba8e8b599c3c4be"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Middleware/Wit.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"75cc76d48f1254b1d2eb1f9200abe9ae441bcef4991af151425876c8c24c3df8"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Cache/RedisCache.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"aa34fef2eb1ac70de0b5de7834eae8653ce596e2e15db1c73a17cd72600f06a2"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Messages/Matcher.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"e3fd18162936002b5b6350c3723a0f1b343b5ed6dddbfed87c6f76131adf8e02"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Commands/ConversationManager.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"ca23511f33f0efeb1554c354e57d4a042722852bb11d0f999a12b4d961106833"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Middleware/MiddlewareManager.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"93fc6a131cd3efee02cc928f62447bcfe585af55715914605d5f1f833b7fd711"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Messages/Outgoing/Question.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"f98573b83edff9d1c3f019ed3eacb4b89c8b2bc0344b2c06933131a641884168"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Drivers/HttpDriver.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"23ebe2c29074bfc43396ee161916c3474163130245b847e1c1a8b92a0468d418"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Storages/Drivers/RedisStorage.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"5b950eef402cb3f5490143f9fb0e9ff173c5d2a2a755b6b0e1fd704f61a8c654"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Storages/Storage.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"b805fcd3bb2688cefbace31fd9fb47eb14251797110e2057b9785e0421ad7793"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Messages/Attachments/Contact.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"438eaae468a6bf5bc0182afd42de584606f706fbe6b5ae0b8c95666b0c54bb99"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Messages/Outgoing/Actions/Button.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"75dcf1f1611012230b7c5ee5b5facf9ab71d910d25ecd275f82e143b4b599af0"}},{"ruleId":"D34","level":"note","message":{"text":"Orphaned file: No one still active in this repository holds living knowledge of this file: everyone who made a meaningful change to it has gone quiet, or what the current team did to it (moves, reformatting, log-level or import edits, renames) never needed understanding it. If it breaks, no one currently understands it. Attach the read to the next change that touches it: have a second person review that change, and leave behind a short comment or test recording what the file is for."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Cache/FileCache.php"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"293c34e42a50f13b31a3be7a5dee5c03cd8906c4afd2aad29dd1ef5d6a57fa70"}},{"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":"bee16a916192bc47803c755fa5c777c01a15fb56e69ce46b43bdf718adcb77fa"},"taxa":[{"id":"CICD-SEC-5","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"ruleId":"D36","level":"note","message":{"text":"A security finding was recorded here. Its details are withheld on the public artifact \u2014 ask the repository owner for the full report."},"partialFingerprints":{"codehealthFindingId/v1":"ace515990e7ee0f17b5c17e44dd168a5bdecf90804a3a3dd845cf05540978013"},"taxa":[{"id":"CICD-SEC-3","toolComponent":{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35"}}]},{"ruleId":"M2","level":"note","message":{"text":"No ADRs: No Architecture Decision Records found \u2014 no conventional ADR directory, no numbered \u0060NNNN-title\u0060 documents in any markup this check reads, and nothing ADR-shaped by content. Design rationale recorded elsewhere (a design-notes tree, a mailing list, pull-request discussion) is not visible to this check and is not re-findable per decision, so a future maintainer cannot ask why one choice was made and get an answer."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"670b3d6e36a756d63097d0dfbf90afd5fc761308800b9354894a07c3f4e4aa14"}},{"ruleId":"M2","level":"note","message":{"text":"No architecture diagram/doc: No C4/Structurizr/PlantUML/Mermaid/Graphviz/D2 diagram, no drawn diagram named for the architecture, no file named \u0060architecture\u0060 or \u0060design\u0060 in any markup this check reads, and nothing in the README, docs or contributor guides that announces the shape \u2014 no \u0060## Architecture\u0060 heading, no \u0022architecture overview\u0022/\u0022high-level design\u0022 phrasing, no \u0022the architecture is \u2026\u0022 introduction, no guided code tour. A shape laid out in prose that never names itself as the architecture is not visible to this check, and neither is one kept outside the repository, so this row reports the absence of a re-findable shape document \u2014 not evidence that nobody wrote the shape down."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"0c190e7c159d1850ee706c3ac4486e151e8a4fe169de4bf61436b9f399654f06"}},{"ruleId":"M4","level":"note","message":{"text":"README/code drift: README claims support exists (PayPal, Open Collective, Patreon) but the repository has no funding or donation infrastructure \u2014 searched for: \u0060donation\u0060. Each was matched case- and separator-insensitively against file and directory NAMES anywhere in the tree, and against the CONTENTS of manifest files (package.json, *.csproj, *.props, *.slnx, *.yml, Dockerfile); the README\u0027s own prose never counts, so a claim is never refuted by merely being made. Nothing outside that search was read \u2014 a footprint living only in a submodule, in a file type not listed here, or under a name none of those terms matches is not seen, and this row is then wrong."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"README.md"},"region":{"startLine":1}}}],"partialFingerprints":{"codehealthFindingId/v1":"343bbbd51ceb8de7a9ff88d79f7c797abf9b5921f8727b67b4d0f4f8a22c5e49"}},{"ruleId":"P1","level":"note","message":{"text":"CI build step not evidenced: A CI pipeline exists but no build step was matched \u2014 changes may merge without the build ever running. A build step may be invoked directly as a command, or declared as a task that a runner named in the pipeline resolves."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"251015acef1a259fcc13b7c60660cc8917ccb3ae8056cb5165ae03312dd586e8"}},{"ruleId":"P12","level":"warning","message":{"text":"Coverage collected but not gated: CI collects a coverage report but no step enforces a minimum \u2014 coverage could halve and CI stays green. Add a step that fails the build when coverage drops below a floor (your coverage tool\u0027s minimum-threshold flag, or a coverage-gate action) so the number guards something. What was searched, so you can tell an absence from a miss: this repository\u0027s CI files AND its coverage configuration \u2014 the well-known coverage and test-runner config files, read at the repository root and inside workspace package directories two levels down, so a floor declared beside the tests rather than in the pipeline is credited \u2014 matched against the threshold settings this check knows by name. A floor set in your coverage service\u0027s web UI rather than in a committed file, or under a setting whose name is not one of those, is not seen here."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"7f63c06044cf998d9a0698692df30d8873e29bc9b0c98ee829255017c1193d33"}},{"ruleId":"P3","level":"note","message":{"text":"No SAST: No static application security testing detected. For this repository\u0027s stack, add phpstan or psalm (or \u0060semgrep --config=auto\u0060, which runs on any language) as a CI step. What was searched, so you can tell an absence from a miss: the 4675 CI workflow file(s) in this repository, and the scanner and linter configuration checked in beside them. A scan that runs outside CI, one configured in your forge\u0027s web UI rather than in a committed file, or a tool whose name is none of those this check carries, is not seen \u2014 if that is your case the row is wrong, and saying so is more useful than adding a second scanner."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"6e54424179c892f03ef2fd003130ac4bd43f39bbf3b1ca0a0143e25acb80ec87"}},{"ruleId":"X10","level":"note","message":{"text":"Duplicated predicate: \u0060function_exists(\u0027igbinary_serialize\u0027) \u0026\u0026 defined(\u0027Redis::SERIALIZER_IGBINARY\u0027)\u0060 appears character-identically in 2 files \u2014 src/Cache/RedisCache.php, src/Storages/Drivers/RedisStorage.php. It is one line, so the duplication detector\u0027s token window never sees it; the copies drift when only one is corrected. Give the condition a name and one home."},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"src/Cache/RedisCache.php"},"region":{"startLine":127}}}],"partialFingerprints":{"codehealthFindingId/v1":"f9b43e237134557b18ae44e230f898d969a27fe24fd5966207f3a65127daa658"}},{"ruleId":"X5","level":"note","message":{"text":"PHP static analysis does not check null everywhere: This repository commits no PHPStan or Psalm configuration (\u0060phpstan.neon[.dist]\u0060, \u0060phpstan.dist.neon\u0060, \u0060psalm.xml[.dist]\u0060, or one a composer.json script names with \u0060--configuration\u0060), so nothing checks null: a \u0060?Customer\u0060 reaches \u0060-\u003Ename()\u0060 silently and throws \u0060Error: Call to a member function name() on null\u0060 the first time it is null. If the build passes a level on a command line instead, put it in a committed configuration \u2014 this check reads configuration, and configuration is all it can read."},"locations":[],"partialFingerprints":{"codehealthFindingId/v1":"4ee877508ebc333ebff2765634874753d7fd67f71ea749a6c333792a59b461d7"}}],"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-1104","guid":"4c918cb5-b2a6-6c55-9963-a44ee464305e","name":"Use of Unmaintained Third Party Components","shortDescription":{"text":"Use of Unmaintained Third Party Components"},"helpUri":"https://cwe.mitre.org/data/definitions/1104.html"},{"id":"CWE-1329","guid":"f70f1c3f-4ccf-cb5e-bdd3-03ba4868d36d","name":"Reliance on Component That is Not Updateable","shortDescription":{"text":"Reliance on Component That is Not Updateable"},"helpUri":"https://cwe.mitre.org/data/definitions/1329.html"},{"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-259","guid":"ae9ad959-fbb6-9d5e-892d-3dca66da0b69","name":"Use of Hard-coded Password","shortDescription":{"text":"Use of Hard-coded Password"},"helpUri":"https://cwe.mitre.org/data/definitions/259.html"},{"id":"CWE-353","guid":"09d7e902-d4ee-f05d-ae6c-0a1554d0c18f","name":"Missing Support for Integrity Check","shortDescription":{"text":"Missing Support for Integrity Check"},"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-77","guid":"332c8ade-6612-9f56-a06b-d8d90b1a8750","name":"Improper Neutralization of Special Elements used in a Command (\u0027Command Injection\u0027)","shortDescription":{"text":"Improper Neutralization of Special Elements used in a Command (\u0027Command Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/77.html"},{"id":"CWE-78","guid":"2e31ceaf-c7ae-2e5e-9661-cfb1362789cf","name":"Improper Neutralization of Special Elements used in an OS Command (\u0027OS Command Injection\u0027)","shortDescription":{"text":"Improper Neutralization of Special Elements used in an OS Command (\u0027OS Command Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/78.html"},{"id":"CWE-79","guid":"fd45580b-e8c4-fc5e-8c2f-aa8fab0b4dbf","name":"Improper Neutralization of Input During Web Page Generation (\u0027Cross-site Scripting\u0027)","shortDescription":{"text":"Improper Neutralization of Input During Web Page Generation (\u0027Cross-site Scripting\u0027)"},"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-89","guid":"6d08fdad-37eb-c150-bbf0-d7d946863407","name":"Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)","shortDescription":{"text":"Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/89.html"},{"id":"CWE-94","guid":"75e7f50c-6c2f-dd52-bf40-bf6c52b861fd","name":"Improper Control of Generation of Code (\u0027Code Injection\u0027)","shortDescription":{"text":"Improper Control of Generation of Code (\u0027Code Injection\u0027)"},"helpUri":"https://cwe.mitre.org/data/definitions/94.html"}]},{"name":"OWASP Top 10","guid":"7d1f0c2e-4b6a-4e39-9a51-0b2e8c3d5f61","version":"2025","organization":"OWASP Foundation","informationUri":"https://top10.owasp.org/2025/","isComprehensive":false,"shortDescription":{"text":"The OWASP Top 10:2025 web application security risks."},"taxa":[{"id":"A03:2025","guid":"168e227c-7735-2e57-bc6e-997affe644c5","name":"Software Supply Chain Failures","shortDescription":{"text":"Software Supply Chain Failures"},"helpUri":"https://top10.owasp.org/2025/A03_2025-Software_Supply_Chain_Failures/"},{"id":"A05:2025","guid":"87e86033-3231-8458-8e4a-5cb82743553a","name":"Injection","shortDescription":{"text":"Injection"},"helpUri":"https://top10.owasp.org/2025/A05_2025-Injection/"},{"id":"A07:2025","guid":"334ddd78-539d-d35c-83dd-253647eb7eaf","name":"Authentication Failures","shortDescription":{"text":"Authentication Failures"},"helpUri":"https://top10.owasp.org/2025/A07_2025-Authentication_Failures/"}]},{"name":"OWASP Top 10 CI/CD Security Risks","guid":"3c9b2a6e-5d14-4f7b-8e02-6a1d9c4b7f35","organization":"OWASP Foundation","informationUri":"https://owasp.org/www-project-top-10-ci-cd-security-risks/","isComprehensive":false,"shortDescription":{"text":"The OWASP Top 10 CI/CD Security Risks."},"taxa":[{"id":"CICD-SEC-3","guid":"458c83a6-ebf7-cf55-8cc3-b4d949bbf271","name":"Dependency Chain Abuse","shortDescription":{"text":"Dependency Chain Abuse"},"helpUri":"https://owasp.github.io/www-project-top-10-ci-cd-security-risks/CICD-SEC-03-Dependency-Chain-Abuse"},{"id":"CICD-SEC-5","guid":"cc7f4610-8859-e654-9029-d5f97d992220","name":"Insufficient PBAC (Pipeline-Based Access Controls)","shortDescription":{"text":"Insufficient PBAC (Pipeline-Based Access Controls)"},"helpUri":"https://owasp.github.io/www-project-top-10-ci-cd-security-risks/CICD-SEC-05-Insufficient-PBAC"},{"id":"CICD-SEC-8","guid":"b6b2dacf-a168-4957-876e-a8c73ee00eb0","name":"Ungoverned Usage of 3rd Party Services","shortDescription":{"text":"Ungoverned Usage of 3rd Party Services"},"helpUri":"https://owasp.github.io/www-project-top-10-ci-cd-security-risks/CICD-SEC-08-Ungoverned-Usage-of-3rd-Party-Services"}]}],"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":10,"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."]}]}