Звіт у форматі JSON машиночитний
{
"data": {
"repo": {
"topics": [],
"is_fork": false,
"size_kb": 565,
"has_wiki": false,
"homepage": "https://guidelines.hihaho.com",
"languages": {
"PHP": 295398,
"Blade": 125
},
"pushed_at": "2026-07-28T13:02:05Z",
"created_at": "2023-09-20T11:04:58Z",
"owner_type": "Organization",
"updated_at": "2026-07-28T13:05:13Z",
"description": "Hihaho PHPStan rules according to our guidelines",
"is_archived": false,
"is_disabled": false,
"license_spdx": "MIT",
"default_branch": "main",
"license_spdx_raw": "MIT",
"primary_language": "PHP",
"significant_languages": [
"PHP"
]
},
"owner": {
"blog": "https://www.hihaho.com/",
"name": "hihaho",
"type": "Organization",
"login": "hihaho",
"company": null,
"location": "Netherlands",
"followers": 5,
"avatar_url": "https://avatars.githubusercontent.com/u/31760627?v=4",
"created_at": "2017-09-08T07:46:13Z",
"is_verified": null,
"public_repos": 9,
"account_age_days": 3245
},
"license": {
"state": "standard",
"spdx_id": "MIT",
"raw_spdx": "MIT",
"file_present": true,
"scorecard_found": true,
"profile_has_license": true
},
"activity": {
"releases": [
{
"tag": "v3.14.0",
"kind": "minor",
"published_at": "2026-07-07T15:53:26Z"
},
{
"tag": "v3.13.0",
"kind": "minor",
"published_at": "2026-06-22T20:30:03Z"
},
{
"tag": "v3.12.0",
"kind": "minor",
"published_at": "2026-06-22T16:40:28Z"
},
{
"tag": "v3.11.2",
"kind": "patch",
"published_at": "2026-06-22T12:19:55Z"
},
{
"tag": "v3.11.1",
"kind": "patch",
"published_at": "2026-06-22T11:44:59Z"
},
{
"tag": "v3.11.0",
"kind": "minor",
"published_at": "2026-06-22T10:42:59Z"
},
{
"tag": "v3.10.0",
"kind": "minor",
"published_at": "2026-06-22T09:12:15Z"
},
{
"tag": "v3.9.0",
"kind": "minor",
"published_at": "2026-06-21T09:39:12Z"
},
{
"tag": "v3.8.0",
"kind": "minor",
"published_at": "2026-06-21T08:10:12Z"
},
{
"tag": "v3.7.0",
"kind": "minor",
"published_at": "2026-06-16T21:16:49Z"
},
{
"tag": "v3.6.1",
"kind": "patch",
"published_at": "2026-06-14T16:52:03Z"
},
{
"tag": "v3.6.0",
"kind": "minor",
"published_at": "2026-06-14T15:33:34Z"
},
{
"tag": "v3.5.0",
"kind": "minor",
"published_at": "2026-06-14T13:22:12Z"
},
{
"tag": "v3.4.1",
"kind": "patch",
"published_at": "2026-06-14T11:02:04Z"
},
{
"tag": "v3.4.0",
"kind": "minor",
"published_at": "2026-06-13T22:59:03Z"
},
{
"tag": "v3.3.0",
"kind": "minor",
"published_at": "2026-06-13T16:41:23Z"
},
{
"tag": "v3.2.0",
"kind": "minor",
"published_at": "2026-06-09T17:21:15Z"
},
{
"tag": "v3.1.2",
"kind": "patch",
"published_at": "2026-04-22T14:50:08Z"
},
{
"tag": "v3.1.1",
"kind": "patch",
"published_at": "2026-04-22T14:42:38Z"
},
{
"tag": "v3.1.0",
"kind": "minor",
"published_at": "2026-04-21T16:31:27Z"
},
{
"tag": "v3.0.0",
"kind": "major",
"published_at": "2026-04-12T15:09:44Z"
},
{
"tag": "v2.2.0",
"kind": "minor",
"published_at": "2026-03-01T16:13:28Z"
},
{
"tag": "v2.1.0",
"kind": "minor",
"published_at": "2025-02-26T11:45:33Z"
},
{
"tag": "v1.2.1",
"kind": "patch",
"published_at": "2024-12-11T15:25:49Z"
},
{
"tag": "v2.0.1",
"kind": "patch",
"published_at": "2024-12-11T15:27:39Z"
},
{
"tag": "v1.2.0",
"kind": "minor",
"published_at": "2024-12-11T14:01:21Z"
},
{
"tag": "v2.0.0",
"kind": "major",
"published_at": "2024-11-27T12:40:55Z"
},
{
"tag": "v1.1.1",
"kind": "patch",
"published_at": "2024-06-19T07:56:38Z"
},
{
"tag": "v1.1.0",
"kind": "minor",
"published_at": "2024-06-18T07:59:16Z"
},
{
"tag": "v1.0.0",
"kind": "major",
"published_at": "2024-05-02T11:38:35Z"
},
{
"tag": "v0.1.0",
"kind": "minor",
"published_at": "2023-09-20T13:28:32Z"
}
],
"recent_commits": [
{
"oid": "4fafc86cb9e719d86102a0c9504ec09351481e55",
"body": "AddSeeTestAnnotationRector writes the fully-qualified test class name;\nPint's fully_qualified_strict_types shortens it back; the next Rector run\nsees no FQCN and appends a duplicate. The two never converge, so\n`composer qa` reported drift on files nobody had touched. Skip the rule.\n\nAlso makes TraitRequiresInterfaceRule readonly, as Rector suggests once\nit stops rewriting that file's docblock.",
"is_bot": false,
"headline": "Stop Rector and Pint fighting over @see annotations",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-07-28T13:02:03Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9b2e9aba0a0b8050fee26ab2e031604be910153b",
"body": "PHP cannot make a trait require an interface, so a trait and the contract\nit is meant to satisfy drift apart silently: classes pick up the trait's\nmethods without ever implementing the interface, and every tool that\nidentifies them by interface skips them.\n\nTraitRequiresInterfaceRule flags a class o\n[…]\nmeter, empty by default. A configured name that does not exist, or\nis not of the expected kind, aborts the analysis rather than matching\nnothing; names are matched case-insensitively, like PHP itself.",
"is_bot": false,
"headline": "Add rule requiring an interface alongside a trait",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-07-28T12:59:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "9e2f2300478f63acde861357057d88dc45aae279",
"body": "…5) (#88)\n\n* ci: trigger auto-fix on push instead of pull_request\n\nResolves CodeQL untrusted-checkout alert #15. The medium\nactions/untrusted-checkout rule fires on any explicit PR-head-ref\ncheckout (a step with ref: containing github.head_ref) in a\npull_request-triggered workflow — it has no permis\n[…]\nmmit straight to main.\n\nAlso folds in the pre-existing PHPStan-verify step that discards an\nauto-fix which would break static analysis before it is committed.\n\n* ci: tighten auto-fix workflow comments",
"is_bot": false,
"headline": "ci: trigger auto-fix on push to resolve CodeQL untrusted-checkout (#1…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-07-07T21:44:02Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "64d51dc73511f7d7f4b4c7668e2796f2ebe057d0",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.14.0 (#87)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-07-07T17:09:19Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4c9bc898792c20b79bcdb7ef2e18043bf18fcd20",
"body": "Flags a non-empty $with property declared on an Eloquent Model. A\nmodel-level $with eager-loads its relations on every query for that\nmodel, globally and invisibly, inflating query counts across endpoints\nthat never need them. An explicit empty $with = [] is ignored, as is a\n$with property on any non-Model class.\n\nRegistered directly (property declarations are rare). Identifier:\nhihaho.conventions.noEloquentWithProperty.",
"is_bot": false,
"headline": "Add rule prohibiting Eloquent $with property",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-07-07T12:08:10Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2abfa1e679fdf631202877f276d34f8aa84639d7",
"body": "Bumps [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action) from 7.1.0 to 7.2.0.\n- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)\n- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.m\n[…]\nirect:production\n update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "build(deps): bump stefanzweifel/git-auto-commit-action (#86)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-07-02T06:39:13Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "386039a4267ec9bf9228791b55b20d49dcb7d065",
"body": "Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.\n- [Release notes](https://github.com/actions/checkout/releases)\n- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/actions/checkout/compare/v6...v7)\n\n---\nupdated-dependenc\n[…]\nirect:production\n update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "build(deps): bump actions/checkout from 6 to 7 (#84)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-06-25T06:24:02Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "308901fb7e452f9af3c100a5c7f7d479a6524ce6",
"body": "Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.\n- [Release notes](https://github.com/actions/cache/releases)\n- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)\n- [Commits](https://github.com/actions/cache/compare/v5...v6)\n\n---\nupdated-dependencies:\n- dependenc\n[…]\nirect:production\n update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "build(deps): bump actions/cache from 5 to 6 (#85)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-06-25T06:23:50Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "561b1323883770343b911cec052e73b50fef7ddf",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.13.0 (#83)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-22T20:43:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "2eb0b0a9cf1b43558266fa005ec7f524913a97d9",
"body": "A stubbedMethods return type of 'static', '$this', or 'self' now binds to\nthe receiver instead of being parsed as a PHPDoc type string, so a stubbed\nfluent method (a $this-returning macro, a chainable Nova field method)\nkeeps its chain typed instead of widening. Resolves the gap where\nself-returning framework methods couldn't be expressed as a fixed string.\n\nAlso adds an optional-{param?} implicit-binding fixture pinning the\ndocumented non-null over-claim.",
"is_bot": false,
"headline": "Add static/$this/self return marker to stubbedMethods (#82)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T20:28:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d7ef62058ac08ed9a70e31c3328af3e9b1b3b090",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.12.0 (#80)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-22T20:10:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a62c99c0b098fb4f29fa5e8f17461f09a7cadc2c",
"body": "PHPStan's SimpleParser (the @currentPhpVersionSimpleDirectParser injected\ninto both route-binding resolvers) already runs NameResolver internally, so\nparsed class names come back fully qualified. The extra NameResolver\ntraversal was a no-op, and the comment claiming the parser does not resolve\nnames was wrong. Drop both.",
"is_bot": false,
"headline": "Remove redundant NameResolver pass from route-binding parsing (#81)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T20:05:35Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "33f1f027b1c3ec89e734692357591b5d4ef1a63f",
"body": "* Add implicit route-model binding resolution\n\nExtend RouteBindingReturnTypeExtension to resolve Laravel implicit\n(controller type-hint) route-model bindings, layered after the explicit\nRoute::model()/Route::bind() provider map. Retires route()+assert pairs\nfor parameters bound implicitly rather tha\n[…]\nassertions in CI. Keep the\nlarastan-loaded test asserting only the extension's own (version-stable)\noutput, and cover the skip/fail-safe cases in a Laravel-only test with a\nstable default type string.",
"is_bot": false,
"headline": "Add implicit route-model binding resolution (#79)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T16:37:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "594e6f66181ef5195af08386c92dc9da1742f7c8",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.11.2 (#78)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-22T12:29:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5d8aaf5c37f0d6627410b831d13a277a1509d222",
"body": "…s (#77)\n\nAdd two README adoption notes, both surfaced by real consumer integration:\n- whereHas/relation closures: returning the narrowed builder trips a\n return.type covariance error; use void block closures.\n- routeBindingProviders: once route('x') is typed, existing\n assert($x instanceof Model)\n[…]\nroaden test fixtures: exercise the full relation-existence method\nfamily (orWhereDoesntHave/doesntHave/has with closures) and a Route::bind\nclosure with a built-in return type (skipped, not narrowed).",
"is_bot": false,
"headline": "Document route() assert-sweep and whereHas void-closure adoption note…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T12:17:31Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "28c15cad21b29be9300ad7b74535a58d3e9cb40f",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.11.1 (#76)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-22T11:53:02Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e06e0f460b4dcc529057b151f0f462ba397e5e49",
"body": "v3.11.0 found zero bindings in any larastan-enabled app (i.e. every real\nLaravel project), silently leaving $request->route('x') as\nobject|string|null. Two root causes, both invisible to the package's own\ntest suite because it had no larastan installed:\n\n- larastan ships a DynamicMethodReturnTypeExt\n[…]\nsion WITH larastan loaded, so this can't regress silently. Exclude\nlarastan from the package's own analysis via the extension-installer\nignore list (it is only needed by the regression test's config).",
"is_bot": false,
"headline": "Fix RouteBindingReturnTypeExtension under larastan (#75)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T11:43:05Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0c52ada1b44fea69a30ef80d8e1f74451346c451",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.11.0 (#74)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-22T10:46:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "41c9213cb43cf8faeb79abea414c37f8de23ad24",
"body": "* Add RouteBindingReturnTypeExtension\n\nType $this->route('x') / $request->route('x') as the model bound to the\nroute parameter, read from the configured route-service providers'\nRoute::model() and Route::bind() calls. Removes the repeated\nroute()+assert($x instanceof Model) pattern; the parameter na\n[…]\nosures with a T|null union return type (not only ?T).\n- Resolve class-constant route-parameter names (e.g. RouteParams::SUBTITLE),\n via the constant's value expression so typed constants resolve too.",
"is_bot": false,
"headline": "Add route-model binding return type extension (#73)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T10:39:14Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1f6f3042a19c201d4bb359ea2e2e28865058c14f",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.10.0 (#72)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-22T09:15:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f9fed69a36a3123273885f216301843ee9061287",
"body": "* Add RelationExistenceClosureBuilderParameterExtension\n\nInfer the related-model builder type for closures passed to Eloquent's\nrelationship-existence methods (whereHas/orWhereHas/whereDoesntHave/\norWhereDoesntHave/has/orHas/doesntHave/orDoesntHave) when the relation is\ngiven as a constant string. P\n[…]\n relation cannot be\n proven, so genuine column typos still fail.\n- Declares illuminate/database, now used directly by the extension.\n\n* Document relation-existence closure builder extension in README",
"is_bot": false,
"headline": "Add relation-existence closure builder type extension (#71)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-22T08:58:30Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "1e752bc30cf4f095ec583c3f67be0e315e7d8317",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.9.0 (#70)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-21T09:40:55Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3bc5796dd436e9f4124dc471e677db14292ebd23",
"body": "Adds a DynamicMethodReturnTypeExtension that types Collection/LazyCollection ->values()->all() as list<TValue> instead of array<int, TValue>, restoring the JSON-array guarantee that the values() re-key provides at runtime.\n\nDetection is syntactic (direct ->values() receiver) and class-guarded (Support\\Collection/LazyCollection or subclass), so bare Enumerable and split-variable chains fail safe. Registered automatically; documented under a new README 'Return type extensions' section.",
"is_bot": false,
"headline": "Add CollectionListAllReturnTypeExtension (#68)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-21T09:30:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0f71d2ccdcc3a174ec692a4b3533e288972c889b",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.8.0 (#67)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-21T08:11:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d68150cd4684be34251cda73d7a6e85d2eec2b98",
"body": "* Add StubbedMethodsClassReflectionExtension\n\nResolve methods that exist at runtime but not in PHPStan reflection — Faker\ncustom providers (added via __call), Laravel macros, facade __callStatic\nforwarding — so they no longer require a baseline entry, while a typo'd method\nname (not in the configure\n[…]\nrop stale 'facade __callStatic' mention from the stubbedMethods comment\n to match the instance-only scope set in the rest of the PR\n\n---------\n\nCo-authored-by: Claude Opus 4.8 <noreply@anthropic.com>",
"is_bot": false,
"headline": "Add StubbedMethodsClassReflectionExtension (#66)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-21T08:05:55Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "b4165abcce6892dbd2576fed3c3b978d1dfdebe3",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.7.0 (#65)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-16T21:26:42Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "db202f2fa990b99b081479507d698dbeac9e88e6",
"body": null,
"is_bot": false,
"headline": "docs: note nested manifest outputPath auto-creates its directory",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-16T20:15:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "568df24d5faf374396967fb69214480abc626117",
"body": "Lets consumers set a nested outputPath like .config/named-arguments-manifest.json;\nWriteNamedArgumentManifestRule now creates the parent directory before writing,\nsince file_put_contents does not create intermediate directories.",
"is_bot": false,
"headline": "feat: create manifest parent directory so nested output paths write",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-16T20:13:37Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "7cab0b77923a91100bad28752ec32b717e2b3f6f",
"body": "Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.37.1 to 2.37.2.\n- [Release notes](https://github.com/shivammathur/setup-php/releases)\n- [Commits](https://github.com/shivammathur/setup-php/compare/7c071dfe9dc99bdf297fa79cb49ea005b9fcadbc...f3e473d116dcccaddc5834248c87\n[…]\nirect:production\n update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "build(deps): bump shivammathur/setup-php from 2.37.1 to 2.37.2 (#52)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-06-14T17:10:22Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "99d74a4e687de3aa3c298aef9da7a2a00d211582",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>\nCo-authored-by: Sander Muller <github@scode.nl>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.6.1 (#64)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-14T17:00:58Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f3e8be2f9a13dfe5460347a1d157e478a920cd39",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.6.0 (#63)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-14T16:52:30Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "344342dd545e9041023752650cf734e5d18cc55f",
"body": "The positional-flag detector resolves a parameter name to report the\nopaque bool/null argument. Parameter names and the variadic flag are\nstatic metadata, independent of the argument types, so the arg-based\nParametersAcceptorSelector::selectFromArgs() — which performs overload\nselection and, for gen\n[…]\nirectly in that case.\n\nAlso replaces the per-call in_array(...) literal in the flag gate with a\nmatch expression, avoiding a 3-element array allocation on every call\nwhose last argument is a constant.",
"is_bot": false,
"headline": "perf: avoid arg-based variant selection for single-variant flag checks",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T16:38:12Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "e473a1b11e69d39630375b500232e3505bf9af25",
"body": "…est (#62)\n\n`$obj?->method(..., true)` (nullsafe calls — common with nullable Laravel\nrelations) were silently skipped by both the flag rule and the manifest\ncollector. Now covered:\n\n- shared instanceCallFlagSite() resolves `$obj->m()` and `$obj?->m()`\n identically (a nullable receiver collapses vi\n[…]\ne\n call in both its null and non-null scopes, so the collector fires twice for\n one site. Errors are auto-deduped by PHPStan; collected data is not, so the\n writer keys records by their full tuple.",
"is_bot": false,
"headline": "feat: cover nullsafe method calls in the positional-flag rule + manif…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T15:24:59Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "6c4d44ef28b5cba27507b359e2136055cd835b36",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.5.0 (#61)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-14T13:37:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cef4ad0a63ae559df4a03ea5f8f2d1ba68ec4411",
"body": "…pe (#60)\n\nAdds an opt-in producer for hihaho/rector-rules' NamedArgumentFromManifestRector,\nwhich names positional bool/null flags at call sites that only resolve under\nlarastan — the gap bare-PHPStan auto-fixers can't reach. That rector rule is\ninert without a JSON manifest; this produces it.\n\n- D\n[…]\n.\n\nSchema verified against NamedArgumentFromManifestRector 0.9.x: file\n(root-relative), line (call-node start), method (name for calls, FQCN for new),\n0-based argIndex, paramName, value (drift guard).",
"is_bot": false,
"headline": "feat: named-argument manifest producer + convention-faithful flag sco…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T13:17:36Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "d1f0129eda09cbff9753fc9a6fd239846bb0d1c4",
"body": "refactor: consolidate debug-detection wrappers into BaseNoDebugRule",
"is_bot": false,
"headline": "Merge pull request #59 from hihaho/refactor/consolidate-debug-wrappers",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T12:11:41Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "672a01a5b0832ee48855350849e94c9484c2293c",
"body": "The func/chained/static debug checks were duplicated between each standalone\ndebug rule and its registered Combined* counterpart. Move the three thin\nwrappers into BaseNoDebugRule (funcDebugError, chainedDebugError,\nstaticDebugError), called by both sides, and absorb the static-debug trait\ninto the \n[…]\n last twin/Combined duplication — every check now has a\nsingle shared implementation. No behaviour change: messages, identifiers,\nand gating are unchanged and the full suite passes without test edits.",
"is_bot": false,
"headline": "refactor: consolidate debug-detection wrappers into BaseNoDebugRule",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T12:09:55Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "fe98cf072ba096f1edba3e457d27fe15c304c19d",
"body": "test-registered-combined-rules.md and dedupe-rule-logic-into-traits.md\nshipped in v3.4.1; no-unsafe-request-data.md was implemented in an earlier\nrelease. All tasks complete — removing the finished spec files.",
"is_bot": false,
"headline": "chore: remove fully-implemented specs (#58)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T11:17:42Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b43afe7b9011838b501c92adddde086abfcb980f",
"body": "The auto-fix PR workflow failed on any PR that produced a working-tree\nchange: it committed composer-install side effects — the environment-specific\n.config/boost/manifest.json regenerated by the post-install boost sync — and\nthen could not push, because checkout runs with persist-credentials: false\n[…]\nmmitted.\n- Restore push credentials only after the PR-controlled rector/pint steps\n have run, so untrusted code still never executes with a write token in git\n config while the auto-commit can push.",
"is_bot": false,
"headline": "ci: fix auto-fix workflow push auth and over-broad commit scope (#57)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T11:12:13Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "006da7bda91283079f6c3864bcb3ba0903a2acf6",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.4.1 (#56)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-14T11:05:45Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "924ca678ac153250e08f20a803f17e2950d0f51f",
"body": "…aits\n\nrefactor: share rule checks via traits and cover Combined rules",
"is_bot": false,
"headline": "Merge pull request #55 from hihaho/refactor/dedupe-rule-logic-into-tr…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T10:57:25Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cb9959923d45bd8d498d7bf0cc2428333acc8156",
"body": "Extract the six duplicated rule checks into shared traits so each\nsingle-responsibility twin and its registered Combined* rule call one\nimplementation, removing the drift surface between them:\n\n- DetectsUnsafeRequestData / DetectsUnsafeRequestFacade /\n DetectsUnsafeRequestHelper, DetectsInvadeUsage\n[…]\nelper calls (e.g. \\REQUEST()) that the twin\nNoUnsafeRequestHelperRule already flagged.\n\nNo behaviour change to the extracted checks; extension.neon and all\npublic constructor signatures are unchanged.",
"is_bot": false,
"headline": "refactor: share rule checks via traits and cover Combined rules",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-14T10:46:29Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "271efb339e17ebe8a2a03700b016e5f9bf5f54a5",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.4.0 (#54)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-13T23:03:21Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "92b67121077aaad66856477d01229a655b3b32db",
"body": "Flag a bare true/false/null literal passed positionally as the last argument\nof a first-party method, static, or constructor call. A positional\nsetActive('name', false) hides the flag's meaning; naming it\n(setActive('name', active: false)) makes the call self-documenting.\n\nIdentifier: hihaho.convent\n[…]\nnfigurable via\n positionalFlagArgument.firstPartyNamespaces)\n\nClassmap the new stub dir and skip it in rector (fixtures must not be\ntransformed — rector strips the empty signature-only stub methods).",
"is_bot": false,
"headline": "feat: add positional flag-argument convention rule",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-13T22:10:46Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "c598125d4abcc8ff44e8a37957b7bce86b242b8f",
"body": "- laravel/pao: agent-optimized output for PHP testing tools\n- nunomaduro/collision: nicer test error reporting\n\nlarastan intentionally omitted — this package analyses with plain PHPStan,\nand larastan auto-registers via extension-installer, which would change its\nown analysis baseline.",
"is_bot": false,
"headline": "chore: add canonical dev deps",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-13T18:41:01Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "05de3aeb307d64fb57c1df97e3bd3d0bab4b411e",
"body": "- rename workflows to canonical names: tests.yml -> run-tests.yml,\n analyzer.yml -> phpstan.yml. Workflow `name:` fields (run-tests / PHPStan)\n and matrix job names are unchanged, so branch-protection check contexts are\n unaffected; updated self-referencing path filters and README badge URLs.\n- c\n[…]\n phpunit.xml.dist); update the\n .gitattributes export-ignore entry to match.\n- add a License badge to the README badge row.\n- export-ignore /.config so the boost config stays out of the dist archive.",
"is_bot": false,
"headline": "chore: align tooling with canonical repo-init setup",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-13T18:41:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "aeb08ee94407d7b322dbdff42354fe5d306289f7",
"body": "Laravel 11 is unfixable on CI: CVE-2026-48019 (PKSA-mdq4-51ck-6kdq) flags\nevery laravel/framework v11 release on Packagist with no patched v11, so\nComposer 2.8 advisory-blocking cannot resolve the ^11.31 matrix legs at all.\n\n- illuminate/support: ^12.0||^13.0 (was ^11.31|^12.0|^13.0)\n- orchestra/tes\n[…]\ns: tightening the illuminate/support constraint only\nnarrows install eligibility. Composer keeps Laravel 11 projects on the last\ncompatible release (3.3.0); no consumer build errors. Ships as a minor.",
"is_bot": false,
"headline": "chore: drop Laravel 11 support",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-13T18:41:01Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "08787956a8c7a7f06e2f8563417254fa7550fb3a",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.3.0 (#53)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-13T17:54:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d3a9e1685ad4e9f746619f26c60f794e1d64c3ee",
"body": "Add post-install-cmd / post-update-cmd autosync hooks and switch sync-ai to\n`boost sync`; refresh the boost manifest. Generated by package-boost-php.",
"is_bot": false,
"headline": "chore: sync package-boost tooling",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-13T16:35:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "37d9a28ac53c1bf08c5c7e86dd8e07f75913b8f2",
"body": "Flag reading a request field inside a FormRequest when the same class's\nrules() never validates it — the inverse of NoUnsafeRequestDataRule, which\nexempts $this reads inside a FormRequest. Identifier:\nhihaho.validation.unvalidatedFormRequestField.\n\nMerged into CombinedMethodCallRule (single-dispatch\n[…]\ne\nhierarchy (a shared base or trait). rules() inherited from a base class is\nfollowed; nested keys match on their root segment. Accessor list is\nconfigurable via unvalidatedFormRequestField.accessors.",
"is_bot": false,
"headline": "feat: add UnvalidatedFormRequestFieldRule",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-13T16:35:23Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "dbab322fe50aff71333dcf25fbb231d775689b50",
"body": "Co-authored-by: SanderMuller <9074391+SanderMuller@users.noreply.github.com>",
"is_bot": true,
"headline": "Update CHANGELOG for v3.2.0 (#51)",
"author_name": "github-actions[bot]",
"author_login": "github-actions[bot]",
"committed_at": "2026-06-09T17:26:24Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c625a78716405a255caef2bf1f05153715b08bb8",
"body": "…1 advisories are unpatched\n\nAll orchestra/testbench v9.x versions require laravel/framework ^11.x, which is\ncurrently blocked by Packagist security advisories on every v11 release through\nv11.54. The advisories are external (appeared after the 2026-05-24 green run)\nand unrelated to this package. Add continue-on-error scoped to the L^11.31 legs\nso CI stays green until Laravel ships a patched v11 release.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "fix(ci): allow L^11.31 matrix legs to fail while laravel/framework v1…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-09T07:09:33Z",
"body_truncated": false,
"is_coding_agent": true
},
{
"oid": "a856f602b4b47c2648e93d867aad3bc5b7f19bae",
"body": "…ation guideline\n\nReplace sandermuller/package-boost with sandermuller/package-boost-php ^1.0\nand sandermuller/boost-skills ^2.4. Move boost config to .config/boost.php\n(supported by boost-core 1.1.1+). Remove all local .ai/skills/ — canonical\nequivalents now come from vendor; phpstan-developer sour\n[…]\nitting directly to main, and the benchmark-table workflow does\nnot exist. Gitignore generated boost outputs (.boost/, .claude/skills/, etc.).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "chore: migrate to package-boost-php + boost-skills, fix release-autom…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-09T07:02:56Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "8d9b4913924116dd9bb292114f4d6d0b573e8f59",
"body": "… from base constants\n\nMove isDebugHelperMethodCall to BaseNoDebugRule so ChainedNoDebugInNamespaceRule\nand CombinedMethodCallRule share a single implementation. Promote\nFUNCTION_DEBUG_STATEMENTS and METHOD_DEBUG_STATEMENTS to protected so Combined\nrules can derive their quick-reject lookups from the authoritative source instead\nof duplicating the set.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "refactor: deduplicate isDebugHelperMethodCall and derive quick-reject…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-09T07:02:24Z",
"body_truncated": false,
"is_coding_agent": true
},
{
"oid": "1a70d6dd4fb984622386e296267b391db46c46b8",
"body": "Rector FlipTypeControlToUseExclusiveTypeRector: replace !== null\nguards with instanceof IdentifierRuleError for explicit typing, and\nstrip redundant readonly keyword on non-promoted properties inside\nfinal readonly classes. Pint fixes spacing in three files.\n\nAlso updates autoresearch/phpstan-rules-progress.md with final results:\noverhead now ≈ 0ms (within ±150ms noise floor of a ~12s benchmark).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "autoresearch: apply Rector + Pint cleanups, update progress log",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:56:54Z",
"body_truncated": false,
"is_coding_agent": true
},
{
"oid": "fab06174e7a9bb7e97b265549c89b2b6dc8aa0bd",
"body": "Adds INTERESTING_FUNC_NAMES const array containing the names of all\nfunctions this rule cares about. Calls whose name is not in the set\nand contains no backslash return [] immediately, skipping all three\nsub-rule checks. Qualified function names (containing backslash) bypass\nthe filter so that edge-cases like qualified request() helpers are still\ncaught via getLast() in checkRequestHelper.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "autoresearch: add quick-reject const lookup in CombinedFuncCallRule",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:52:02Z",
"body_truncated": false,
"is_coding_agent": true
},
{
"oid": "e883fc9e889874bce339ee517a1e8ba6bb6795c6",
"body": "Precompute a merged lookup of debug method names + unsafe request method\nnames (both lowercased). A single isset in processNode short-circuits\nthe two sub-rule checks for the ~99% of method calls that are neither\ndebug-related nor unsafe request accessors, avoiding the strtolower +\nmethod dispatch overhead for every non-interesting method call.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "autoresearch: add quick-reject lookup in CombinedMethodCallRule",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:49:58Z",
"body_truncated": false,
"is_coding_agent": true
},
{
"oid": "6ea2c19e842a534b380a66a3d5729c55561a37bc",
"body": "Raise cognitive_complexity.class from 12→35 (combined rules merging 3\nsub-rules are intentionally more complex by design), extract processNode\nlogic into private methods to keep each function under 10, add\nmatchDebugNamespace helper to BaseNoDebugRule shared by all debug rules,\nfix property.notFound\n[…]\nto accept Name\ninstead of StaticCall, add @extends generics PHPDoc to combined rules,\nand add @var annotation to static classIsRequest cache.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
"is_bot": false,
"headline": "autoresearch: fix all 25 phpstan errors in combined rules",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:44:27Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "fd41f129f2d58bcb2b709d936ec437bdefb69dbb",
"body": "… to eliminate 1/2 dispatch overhead",
"is_bot": false,
"headline": "autoresearch: OOB3 merge MethodCall rules into CombinedMethodCallRule…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:23:53Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "d8df31735d2f15d8c65a5ca737a7ec8504a085b7",
"body": "…eliminate 2/3 dispatch overhead",
"is_bot": false,
"headline": "autoresearch: OOB1 merge FuncCall rules into CombinedFuncCallRule to …",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:22:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "6a86c20972231b7b9aad4c9ad65140595feb995d",
"body": "… to eliminate 2/3 dispatch overhead",
"is_bot": false,
"headline": "autoresearch: OOB2 merge StaticCall rules into CombinedStaticCallRule…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:18:57Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "a2f824e76272826d548641d3ba6ef1b9afc340de",
"body": "…), str_contains for multi-part check",
"is_bot": false,
"headline": "autoresearch: OOB4+B9 use ->name direct property instead of toString(…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:16:13Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c11f1893dde4c36eeab05091cfa63136cb8e3195",
"body": "…e to skip strtolower for non-Request calls",
"is_bot": false,
"headline": "autoresearch: B8 add getLast() pre-filter in NoUnsafeRequestFacadeRul…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:02:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0c51a1b55ceb73c62036c5c3aadddfde041f8b22",
"body": "…n ChecksNamespace",
"is_bot": false,
"headline": "autoresearch: B6 get namespace once per namespaceStartsWithAny call i…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:01:06Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5fb7c122ab4c0c0cfff6c314328ab404890804ac",
"body": "…questDataRule",
"is_bot": false,
"headline": "autoresearch: B5 cache ObjectType result per class name in NoUnsafeRe…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T22:00:04Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "90fe9934e424822cdbed11ce521f12c93ee28885",
"body": "…gInNamespaceRule constructor",
"is_bot": false,
"headline": "autoresearch: B4 cache Facade class reflection in StaticChainedNoDebu…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T21:59:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "79fbb3b14ed2d960108c61b0c6d4aed163008cdf",
"body": "… in NoUnsafeRequestHelperRule",
"is_bot": false,
"headline": "autoresearch: B2 move namespace check before ReflectionProvider calls…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T21:57:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1c3a4984b37f6ec67e475deb9e2c1325db3210af",
"body": "…edNoDebugInNamespaceRule",
"is_bot": false,
"headline": "autoresearch: B1 move namespace check before type resolution in Chain…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T21:57:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "082b15fb5745fbcb7f8224bc7d62686a74aaf7c7",
"body": "…cChainedNoDebugInNamespaceRule",
"is_bot": false,
"headline": "autoresearch: B1 move namespace check before type resolution in Stati…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T21:56:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9ab35987cb11dd31e8d0e320f654e1516a9b54a7",
"body": null,
"is_bot": false,
"headline": "autoresearch: B3 replace in_array with isset hash map in BaseNoDebugRule",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T21:54:40Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "1961c55272c0bc919f678a6e8a828def68c2a4d5",
"body": "…liasInBlade",
"is_bot": false,
"headline": "autoresearch: B7 add static ReflectionClass cache in OnlyAllowFacadeA…",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-06-08T21:50:28Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "9cb0dfd945f58ee217c4cb4ff1df4029c33c8df7",
"body": "Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.37.0 to 2.37.1.\n- [Release notes](https://github.com/shivammathur/setup-php/releases)\n- [Commits](https://github.com/shivammathur/setup-php/compare/accd6127cb78bee3e8082180cb391013d204ef9f...7c071dfe9dc99bdf297fa79cb49e\n[…]\nirect:production\n update-type: version-update:semver-patch\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "Bump shivammathur/setup-php from 2.37.0 to 2.37.1 (#50)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-05-24T12:42:39Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "280f0d8e24bfd68daffdb044f1003029e3e429fe",
"body": "Updates the requirements on [sandermuller/package-boost](https://github.com/sandermuller/package-boost) to permit the latest version.\n- [Release notes](https://github.com/sandermuller/package-boost/releases)\n- [Changelog](https://github.com/SanderMuller/package-boost/blob/main/CHANGELOG.md)\n- [Commi\n[…]\ndency-version: 0.15.0\n dependency-type: direct:development\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "Update sandermuller/package-boost requirement from ^0.11 to ^0.15 (#49)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-05-14T09:09:26Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "f93af97626327af9774706a9bc25db46d00bf79c",
"body": "Updates the requirements on [sandermuller/package-boost](https://github.com/sandermuller/package-boost) to permit the latest version.\n- [Release notes](https://github.com/sandermuller/package-boost/releases)\n- [Changelog](https://github.com/SanderMuller/package-boost/blob/main/CHANGELOG.md)\n- [Commi\n[…]\ndency-version: 0.11.0\n dependency-type: direct:development\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "Update sandermuller/package-boost requirement from ^0.9 to ^0.11 (#48)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-05-07T08:07:37Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "07e0f29ef00923608dd28134323b52b15a6512e8",
"body": "Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.8 to 8.1.1.\n- [Release notes](https://github.com/peter-evans/create-pull-request/releases)\n- [Commits](https://github.com/peter-evans/create-pull-request/compare/271a8d0340265f705b14b6d32b9829c1cb33d\n[…]\nirect:production\n update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>",
"is_bot": true,
"headline": "Bump peter-evans/create-pull-request from 7.0.8 to 8.1.1 (#47)",
"author_name": "dependabot[bot]",
"author_login": "dependabot[bot]",
"committed_at": "2026-04-23T12:47:12Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "5696ddfd7fa16a6f3016742a4e0ec778f72b7057",
"body": "Backfill entries that the Update Changelog workflow should have\ncommitted on each release but couldn't (broken create-pull-request\nSHA for v3.1.1; Duplicate Authorization header for v3.1.2). Content\nis the release body verbatim for each tag.",
"is_bot": false,
"headline": "Update CHANGELOG for v3.1.1 and v3.1.2 (#46)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-22T14:59:33Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "42d2e9e60c0a3effaefaab0e77fad0c23f01199a",
"body": "actions/checkout persists its token as an Authorization header by\ndefault; peter-evans/create-pull-request then adds its own token,\nproducing a Duplicate header: Authorization and failing with\ngit exit code 128. Disabling credential persistence on checkout\nlets create-pull-request own the remote-auth entirely.",
"is_bot": false,
"headline": "fix: disable persist-credentials on checkout in update-changelog",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-22T14:53:20Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "0bda21fbca9954cf7cd68a2e9d4853005bf81912",
"body": "actions/checkout persists its token as an Authorization header by\ndefault; peter-evans/create-pull-request then adds its own token,\nproducing a Duplicate header: Authorization and failing with\ngit exit code 128. Disabling credential persistence on checkout\nlets create-pull-request own the remote-auth entirely.",
"is_bot": false,
"headline": "fix: disable persist-credentials on checkout in update-changelog",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-22T14:52:12Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "4d47ab987fafe06dd63126f5cbfadc77e1cc1c7e",
"body": "Prior pin referenced a SHA that does not exist in the action repo\n(typo in the second half of the hash), which broke the Update\nChangelog workflow on the v3.1.1 release.",
"is_bot": false,
"headline": "fix: correct pinned SHA for peter-evans/create-pull-request@v7.0.8",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-22T14:48:48Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "c681f4705795fa3e2dcb8720127e2f6617ccea53",
"body": "Each rule processes every FuncCall / MethodCall / StaticCall PHPStan\nvisits, so the filters before the \"real\" check fire on the order of\n10^4 times for a medium codebase. Small constant-factor cleanups here:\n\n- NoUnsafeRequestHelperRule: short-name prefilter (`strtolower(getLast())\n !== 'request'`)\n[…]\ny checks share one build.\n\nNo behavioural change. All five rules remain `final readonly class`;\nno public API surface touched. `.gitignore` ignores the `/autoresearch/`\nbenchmarking scratch directory.",
"is_bot": false,
"headline": "perf: reduce per-node work in request-rule hot paths",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-22T14:40:50Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "0dac2572da59a784f1d6d88d2854014f7a18dab3",
"body": null,
"is_bot": false,
"headline": "Update README.md",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T18:53:15Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b9eee99cfebc85c5cd14e6b6b59dc87da76bcf7f",
"body": "Three individual rule subsections collapse into one Request-validation\ngroup with a summary table, one shared code example, and two\nsubsections (Configuration, Adopting on an existing codebase). Net\nsize drop roughly 230 -> 140 lines while keeping all identifiers,\nexemption rules, defaults, and fiel\n[…]\nt unreliability),\ncollectiq (InputBag escape hatch, dead-code surfacing), and hihaho\n(FormRequest-wrong-key mental model trap, validated()-strips-nested)\nfield reports from the v3.1.0 adoption passes.",
"is_bot": false,
"headline": "Restructure Request-rules section, humanize adoption prose",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T18:51:41Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ab78fa06bc36809412d9559e300a073f01961313",
"body": "* Fix update-changelog workflow + backfill v3.1.0 entry\n\nThe `Update Changelog` workflow failed on the v3.1.0 release because\n`stefanzweifel/git-auto-commit-action` pushes directly to `main`, which\nis a protected branch requiring PRs (GH006: Protected branch update\nfailed).\n\nSwitching to `peter-evan\n[…]\n\n\n* Update .github/workflows/update-changelog.yml\n\nCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>\n\n---------\n\nCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>",
"is_bot": false,
"headline": "Fix update-changelog workflow + backfill v3.1.0 entry (#45)",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T18:43:17Z",
"body_truncated": true,
"is_coding_agent": true
},
{
"oid": "d44908374e09067f79f1687e1d5ae11e6afb8e82",
"body": null,
"is_bot": false,
"headline": "Draft v3.1.0 release notes",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T16:33:03Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "e9d92adbb03ca107f29d060cf786d6f98a399837",
"body": null,
"is_bot": false,
"headline": "Update UPGRADING.md",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T16:30:51Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "5fe2237a4d503c03fd6db0d49b319d4bf71f6fe0",
"body": null,
"is_bot": false,
"headline": "sync-ai",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T16:29:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "fe3434fde806d5a8ab27a3a45a8b6229f2602f7e",
"body": "Data rule tip now explicitly names $request->safe()->string('key') as\nthe ergonomic migration path for $request->string() — ValidatedInput\nhas matching typed accessors, so the Stringable/int/bool chaining\npattern survives the move to validated data. Collectiq dogfood\nsurfaced 4 such cases where the \n[…]\naveat: those architectures push input handling through component\nproperties and form schemas — outside the rule's Request-method\ntargets. A low hit count there is structural, not proof of\ncleanliness.",
"is_bot": false,
"headline": "Sharpen tip messages, document Livewire/Filament caveat",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T16:15:54Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "bd2f17fa7ac129bbe78a5b4cd6d5cca958bb620a",
"body": "File uploads are the highest-consequence unvalidated-input surface\n(size, mime, content). hihaho dogfood surfaced a live case reading\nplayer-uploaded attachments via Request::file() with zero flags\nbecause file readers were missing from the default list. Added\nfile and allFiles to extension.neon def\n[…]\nck types. The existing rule\ndesign relied on it implicitly (and hihaho has real fixtures using\ntoArray(mixed \\$request) with docblock @param Illuminate\\Http\\Request),\nbut it was not explicitly tested.",
"is_bot": false,
"headline": "Add file/allFiles to unsafeMethods defaults, test docblock receiver",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T15:57:58Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "354f61f0bc960109d3a8ced9341b25f60379fd3c",
"body": null,
"is_bot": false,
"headline": "composer qa",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T15:54:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "82a88ac9dd9ed98ddc43e83179f1f0491065a8ce",
"body": "Fortify / auth response classes implement contract-dictated signatures\nlike LoginResponse::toResponse(Request $request). Signature is fixed by\nthe interface, same structural reason as App\\\\Providers — no validation\nboundary inside the class. Defaulting matches that contract pattern.\n\nApp\\\\Http\\\\Resources stays opt-in: whether a JsonResource should read\nraw request at all is a legitimate architectural debate, not a\nframework-forced pattern.",
"is_bot": false,
"headline": "Default-exclude App\\\\Http\\\\Responses",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T14:30:56Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "db32fad711564578ff0eeb7db33a988dbc913e96",
"body": "NoUnsafeRequestHelperRule now interpolates the literal string argument\ninto the error message (request('key')) when available, falling back to\nrequest(...) for dynamic keys. Makes triage grep-friendly when a\ncodebase has many helper-form hits.\n\nREADME \"Expected baseline categories\" section gets an inline\n@phpstan-ignore example for the dynamic-key admin CRUD pattern, so\nconsumers have a clear alternative to baseline-file suppression.",
"is_bot": false,
"headline": "Sharpen helper-rule message + document inline ignore pattern",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T14:24:23Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "120da0078e8566335d73c4529663c56d107d21dd",
"body": "Framework bootstrap code (RateLimiter::for throttle closures, Fortify\nresponse bindings, service provider setup) receives the raw Request by\nLaravel design and has no FormRequest entry point. Excluding\nApp\\\\Providers by default removes that noise while leaving all three\nrules opt-inable for project-specific framework-adapter layers.\n\nChecksNamespace gains a namespaceStartsWithAny() helper shared by all\nthree rules to keep per-rule cognitive complexity under the package cap.",
"is_bot": false,
"headline": "Add excludeNamespaces config, default App\\\\Providers",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T13:55:11Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "071ba1db3e1f315f7a821ddb49a6d69e6484cf5e",
"body": "Per mijntp dogfood pass: codebases will legitimately baseline admin\ndynamic-key CRUD, pre-validation RateLimiter closures, Fortify response\ncontracts, and JsonResource::toArray. Call these out explicitly as\nexpected territory rather than rule bugs.\n\nAlso clarify that the scope-class exemption uses PHPStan inheritance\nresolution, so custom base FormRequest classes work transparently.",
"is_bot": false,
"headline": "Document expected-baseline categories",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T13:42:22Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "cbb97ed6409a1ce582c00ac45b7029122c3f5f74",
"body": "Three rules preventing unvalidated reads from Illuminate\\Http\\Request\nin application code. Use-validated-data-only is enforced at MethodCall\non Request/FormRequest receivers, FuncCall on request('key') direct-arg\nhelper, and StaticCall on the Illuminate\\Support\\Facades\\Request facade.\n\nRaw reads ins\n[…]\n— that is where validation sources its data.\n\nConfig: noUnsafeRequestData.{unsafeMethods, namespaces}. Identifiers:\nhihaho.validation.noUnsafeRequestData, noUnsafeRequestHelper,\nnoUnsafeRequestFacade.",
"is_bot": false,
"headline": "Add NoUnsafeRequestData / Helper / Facade rules",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T13:26:30Z",
"body_truncated": true,
"is_coding_agent": false
},
{
"oid": "ff3a883ba0168deda6c8f4d107f1a13292605f25",
"body": null,
"is_bot": false,
"headline": "Update composer.json",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T11:08:16Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "b8f00aa94765f06255d337118ffe117e79599409",
"body": null,
"is_bot": false,
"headline": "Update .gitignore",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T11:08:14Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "f6f4195da993c5c90dd5094e1d47003abf206de8",
"body": null,
"is_bot": false,
"headline": "Update composer.json",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T08:28:39Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "3d626e99d0b5f93bf727aa2f91d9718b18275ec0",
"body": null,
"is_bot": false,
"headline": "Update composer.json",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-21T08:28:34Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "48aa577e97622c7e194eef504589c3db2a12f6b5",
"body": null,
"is_bot": false,
"headline": "Update README.md",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-19T12:47:00Z",
"body_truncated": false,
"is_coding_agent": false
},
{
"oid": "dc217dd944f1b35d07db3c8a024599a6c143847d",
"body": null,
"is_bot": false,
"headline": "Update README.md",
"author_name": "Sander Muller",
"author_login": "SanderMuller",
"committed_at": "2026-04-12T16:35:55Z",
"body_truncated": false,
"is_coding_agent": false
}
],
"releases_count": 31,
"commits_last_year": 106,
"latest_release_at": "2026-07-07T15:53:26Z",
"latest_release_tag": "v3.14.0",
"releases_from_tags": false,
"days_since_last_push": 0,
"active_weeks_last_year": 15,
"days_since_latest_release": 20,
"mean_days_between_releases": 2.3
},
"community": {
"has_readme": true,
"has_license": true,
"has_description": true,
"has_contributing": true,
"health_percentage": 50,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"ecosystem": {
"packages": [
{
"name": "hihaho/phpstan-rules",
"exists": true,
"license": "MIT",
"keywords": [],
"ecosystem": "packagist",
"matches_repo": true,
"registry_url": "https://packagist.org/packages/hihaho/phpstan-rules",
"is_deprecated": false,
"latest_version": "v3.14.0",
"repository_url": "https://github.com/hihaho/phpstan-rules",
"versions_count": 31,
"total_downloads": 52119,
"dependents_count": 1,
"deprecation_note": null,
"maintainers_count": null,
"monthly_downloads": 5296,
"first_published_at": null,
"latest_published_at": "2026-07-07T12:08:10Z",
"latest_version_yanked": null,
"days_since_latest_publish": 21
}
]
},
"popularity": {
"forks": 1,
"stars": 7,
"watchers": 5,
"fork_history": {
"days": [
{
"date": "2024-10-30",
"count": 1
}
],
"complete": true,
"collected": 1,
"total_forks": 1
},
"star_history": null,
"open_issues_and_prs": 0
},
"ai_readiness": {
"has_nix": false,
"example_dirs": [],
"has_llms_txt": false,
"has_dockerfile": false,
"has_mcp_signal": true,
"bootstrap_files": [],
"api_schema_files": [],
"has_devcontainer": false,
"typecheck_configs": [],
"toolchain_manifests": [],
"largest_source_bytes": 17604,
"source_files_sampled": 193,
"oversized_source_files": 0,
"agent_instruction_files": [
".github/copilot-instructions.md",
"AGENTS.md",
"CLAUDE.md"
],
"agent_instruction_max_bytes": 12530
},
"dependencies": {
"manifests": [
"composer.json"
],
"advisories": {
"error": "No resolved dependencies carried a version and a supported ecosystem",
"scope": "repository_graph",
"source": null,
"findings": [],
"collected": false,
"malicious": [],
"truncated": false,
"by_severity": {},
"advisory_count": 0,
"affected_count": 0,
"assessed_count": 0,
"malicious_count": 0,
"assessed_package": null,
"unassessed_count": 25,
"direct_affected_count": 0
},
"ecosystems": [
"packagist"
],
"dependencies": [
{
"name": "illuminate/database",
"manifest": "composer.json",
"ecosystem": "packagist",
"version_constraint": "^12.0||^13.0"
},
{
"name": "illuminate/http",
"manifest": "composer.json",
"ecosystem": "packagist",
"version_constraint": "^12.0||^13.0"
},
{
"name": "illuminate/support",
"manifest": "composer.json",
"ecosystem": "packagist",
"version_constraint": "^12.0||^13.0"
}
],
"all_dependencies": {
"error": null,
"source": "github-sbom",
"packages": [
{
"name": "illuminate/database",
"direct": true,
"version": null,
"ecosystem": "packagist"
},
{
"name": "illuminate/http",
"direct": true,
"version": null,
"ecosystem": "packagist"
},
{
"name": "illuminate/support",
"direct": true,
"version": null,
"ecosystem": "packagist"
},
{
"name": "larastan/larastan",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "laravel/pao",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "laravel/pint",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "nikic/php-parser",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "nunomaduro/collision",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "orchestra/testbench",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "php",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/extension-installer",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/phpstan",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/phpstan-deprecation-rules",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/phpstan-phpunit",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpstan/phpstan-strict-rules",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "phpunit/phpunit",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "rector/rector",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "rector/type-perfect",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "sandermuller/boost-skills",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "sandermuller/package-boost-php",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "spatie/invade",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "spaze/phpstan-disallowed-calls",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "symplify/phpstan-extensions",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "tomasvotruba/cognitive-complexity",
"direct": false,
"version": null,
"ecosystem": "packagist"
},
{
"name": "tomasvotruba/type-coverage",
"direct": false,
"version": null,
"ecosystem": "packagist"
}
],
"collected": true,
"truncated": false,
"total_count": 25,
"direct_count": 3,
"indirect_count": 22
}
},
"maintainership": {
"issues": {
"open_prs": 0,
"merged_prs": 83,
"open_issues": 0,
"closed_ratio": null,
"closed_issues": 0,
"closed_unmerged_prs": 5
},
"bus_factor": 1,
"bot_contributors": 2,
"top_contributors": [
{
"type": "User",
"login": "SanderMuller",
"commits": 109,
"avatar_url": "https://avatars.githubusercontent.com/u/9074391?v=4"
},
{
"type": "User",
"login": "RobertBoes",
"commits": 32,
"avatar_url": "https://avatars.githubusercontent.com/u/2871897?v=4"
},
{
"type": "User",
"login": "Treggats",
"commits": 21,
"avatar_url": "https://avatars.githubusercontent.com/u/27585?v=4"
}
],
"contributors_sampled": 3,
"top_contributor_share": 0.673
},
"quality_signals": {
"has_ci": true,
"has_tests": true,
"ci_workflows": [
"auto-fix.yml",
"phpstan.yml",
"run-tests.yml",
"update-changelog.yml"
],
"has_docs_dir": false,
"linter_configs": [],
"has_editorconfig": true,
"has_linter_config": false,
"has_precommit_config": false
},
"security_signals": {
"lockfiles": [],
"scorecard": {
"checks": [
{
"name": "Binary-Artifacts",
"score": 10,
"reason": "no binaries found in the repo",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
},
{
"name": "Branch-Protection",
"score": 6,
"reason": "branch protection is not maximal on development and all release branches",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
},
{
"name": "CI-Tests",
"score": 10,
"reason": "25 out of 25 merged PRs checked by a CI test -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
},
{
"name": "CII-Best-Practices",
"score": 0,
"reason": "no effort to earn an OpenSSF best practices badge detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
},
{
"name": "Code-Review",
"score": 0,
"reason": "Found 0/15 approved changesets -- score normalized to 0",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
},
{
"name": "Contributors",
"score": 10,
"reason": "project has 4 contributing companies or organizations",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
},
{
"name": "Dangerous-Workflow",
"score": null,
"reason": "no workflows found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
},
{
"name": "Dependency-Update-Tool",
"score": 10,
"reason": "update tool detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
},
{
"name": "Fuzzing",
"score": 0,
"reason": "project is not fuzzed",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
},
{
"name": "License",
"score": 10,
"reason": "license file detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
},
{
"name": "Maintained",
"score": 10,
"reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
},
{
"name": "Packaging",
"score": null,
"reason": "packaging workflow not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
},
{
"name": "Pinned-Dependencies",
"score": null,
"reason": "no dependencies found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
},
{
"name": "SAST",
"score": 10,
"reason": "SAST tool is run on all commits",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
},
{
"name": "Security-Policy",
"score": 0,
"reason": "security policy file not detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
},
{
"name": "Signed-Releases",
"score": null,
"reason": "no releases found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
},
{
"name": "Token-Permissions",
"score": null,
"reason": "No tokens found",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
},
{
"name": "Vulnerabilities",
"score": 10,
"reason": "0 existing vulnerabilities detected",
"documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
}
],
"commit": "4fafc86cb9e719d86102a0c9504ec09351481e55",
"ran_at": "2026-07-28T15:06:32Z",
"aggregate_score": 6.7,
"scorecard_version": "v5.5.0"
},
"has_codeql_workflow": false,
"has_security_policy": false,
"has_dependabot_config": true
},
"contribution_flow": {
"collected": true,
"ci_last_run_at": "2026-07-28T13:03:02Z",
"oldest_open_prs": [],
"last_merged_pr_at": "2026-07-07T21:44:02Z",
"ci_last_conclusion": "SUCCESS",
"oldest_open_issues": []
}
},
"config": {
"disabled_metrics": [],
"disabled_categories": [],
"disabled_components": {}
},
"source": {
"url": "https://github.com/hihaho/phpstan-rules",
"host": "github.com",
"name": "phpstan-rules",
"owner": "hihaho"
},
"metrics": {
"overall": {
"key": "overall",
"band": "moderate",
"name": "Overall health",
"note": null,
"notes": [],
"value": 65,
"inputs": {
"security": 67,
"vitality": 84,
"community": 44,
"governance": 60,
"engineering": 66
},
"components": []
},
"categories": [
{
"key": "vitality",
"band": "good",
"name": "Vitality",
"value": 84,
"weight": 0.22,
"metrics": [
{
"key": "development_activity",
"band": "good",
"name": "Development activity",
"note": null,
"notes": [],
"value": 74,
"inputs": {
"commits_last_year": 106,
"human_commit_share": 0.75,
"days_since_last_push": 0,
"active_weeks_last_year": 15
},
"components": [
{
"key": "push_recency",
"name": "Push recency",
"detail": "last push 0 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "push_recency",
"params": {
"days": 0
}
}
],
"max_points": 36
},
{
"key": "commit_cadence",
"name": "Commit cadence",
"detail": "15/52 weeks with commits",
"points": 10.4,
"status": "partial",
"details": [
{
"code": "commit_cadence_weeks",
"params": {
"weeks": 15
}
}
],
"max_points": 36
},
{
"key": "commit_volume",
"name": "Commit volume",
"detail": "106 commits in the last year",
"points": 18,
"status": "met",
"details": [
{
"code": "commits_last_year",
"params": {
"count": 106
}
}
],
"max_points": 18
},
{
"key": "openssf_scorecard_maintained",
"name": "OpenSSF Scorecard: Maintained",
"detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "release_discipline",
"band": "excellent",
"name": "Release discipline",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_signed_releases"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 100,
"inputs": {
"releases_count": 31,
"latest_release_tag": "v3.14.0",
"releases_from_tags": false,
"days_since_latest_release": 20,
"mean_days_between_releases": 2.3
},
"components": [
{
"key": "ships_releases",
"name": "Ships releases",
"detail": "31 releases published",
"points": 27,
"status": "met",
"details": [
{
"code": "releases_published",
"params": {
"count": 31
}
}
],
"max_points": 27
},
{
"key": "release_recency",
"name": "Release recency",
"detail": "latest release 20 days ago",
"points": 36,
"status": "met",
"details": [
{
"code": "release_recency",
"params": {
"days": 20
}
}
],
"max_points": 36
},
{
"key": "release_cadence",
"name": "Release cadence",
"detail": "a release every ~2.3 days",
"points": 27,
"status": "met",
"details": [
{
"code": "release_cadence",
"params": {
"gap": 2.3
}
}
],
"max_points": 27
},
{
"key": "openssf_scorecard_signed_releases",
"name": "OpenSSF Scorecard: Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "abandonment",
"band": "excellent",
"name": "Abandonment",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"cap": null,
"state": "maintained",
"guards": [],
"signals": [],
"red_flag": false,
"multiplier_pct": 100,
"declared_reason": null,
"unverified_reason": null,
"unanswered_open_prs": null,
"unanswered_open_issues": null,
"days_since_last_merged_pr": null,
"days_since_last_human_commit": 0,
"days_since_last_human_commit_is_floor": false
},
"components": [
{
"key": "project_is_still_maintained",
"name": "Project is still maintained",
"detail": "last human commit 0 days ago",
"points": 100,
"status": "met",
"details": [
{
"code": "abandonment_maintained",
"params": {
"days": 0
}
}
],
"max_points": 100
}
]
}
],
"description": "Is the project alive — is code being written and are releases shipping?"
},
{
"key": "community",
"band": "at_risk",
"name": "Community & Adoption",
"value": 44,
"weight": 0.18,
"metrics": [
{
"key": "popularity",
"band": "critical",
"name": "Popularity & adoption",
"note": null,
"notes": [],
"value": 16,
"inputs": {
"forks": 1,
"stars": 7,
"watchers": 5,
"growth_state": "unverified",
"growth_factor_pct": 100,
"growth_unverified_reason": "no_history"
},
"components": [
{
"key": "stars",
"name": "Stars",
"detail": "7 stars",
"points": 12.6,
"status": "partial",
"details": [
{
"code": "stars",
"params": {
"count": 7
}
}
],
"max_points": 60
},
{
"key": "forks",
"name": "Forks",
"detail": "1 forks",
"points": 0,
"status": "missed",
"details": [
{
"code": "forks",
"params": {
"count": 1
}
}
],
"max_points": 25
},
{
"key": "watchers",
"name": "Watchers",
"detail": "5 watchers",
"points": 3.3,
"status": "partial",
"details": [
{
"code": "watchers",
"params": {
"count": 5
}
}
],
"max_points": 15
}
]
},
{
"key": "community_health",
"band": "good",
"name": "Community health",
"note": null,
"notes": [],
"value": 70,
"inputs": {
"has_readme": true,
"has_license": true,
"has_contributing": true,
"has_issue_template": false,
"has_code_of_conduct": false,
"has_pull_request_template": false
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 22.5,
"status": "met",
"details": [],
"max_points": 22.5
},
{
"key": "license",
"name": "License",
"detail": "recognized license (MIT)",
"points": 22.5,
"status": "met",
"details": [
{
"code": "license_standard",
"params": {}
},
{
"code": "license_spdx",
"params": {
"spdx": "MIT"
}
}
],
"max_points": 22.5
},
{
"key": "contributing_guide",
"name": "CONTRIBUTING guide",
"detail": null,
"points": 18,
"status": "met",
"details": [],
"max_points": 18
},
{
"key": "code_of_conduct",
"name": "Code of conduct",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 13.5
},
{
"key": "issue_template",
"name": "Issue template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.2
},
{
"key": "pr_template",
"name": "PR template",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 6.3
}
]
},
{
"key": "ecosystem_adoption",
"band": "moderate",
"name": "Ecosystem adoption (downloads)",
"note": null,
"notes": [],
"value": 52,
"inputs": {
"packages": [
"hihaho/phpstan-rules"
],
"dependents": 1,
"ecosystems": "packagist",
"total_downloads": 52119,
"monthly_downloads": 5296
},
"components": [
{
"key": "monthly_downloads",
"name": "Monthly downloads",
"detail": "5,296 downloads/month across packagist",
"points": 49.7,
"status": "partial",
"details": [
{
"code": "downloads_monthly",
"params": {
"count": 5296,
"ecosystems": "packagist"
}
}
],
"max_points": 80
},
{
"key": "registry_dependents",
"name": "Registry dependents",
"detail": "1 packages depend on it",
"points": 2,
"status": "partial",
"details": [
{
"code": "registry_dependents",
"params": {
"count": 1
}
}
],
"max_points": 20
}
]
}
],
"description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
},
{
"key": "governance",
"band": "moderate",
"name": "Sustainability & Governance",
"value": 60,
"weight": 0.24,
"metrics": [
{
"key": "maintainer_resilience",
"band": "at_risk",
"name": "Maintainer resilience (bus factor)",
"note": null,
"notes": [],
"value": 30,
"inputs": {
"bus_factor": 1,
"contributors_sampled": 3,
"top_contributor_share": 0.673
},
"components": [
{
"key": "bus_factor",
"name": "Bus factor",
"detail": "1 contributor(s) cover half of all commits",
"points": 9,
"status": "partial",
"details": [
{
"code": "bus_factor",
"params": {
"count": 1
}
}
],
"max_points": 54
},
{
"key": "commit_distribution",
"name": "Commit distribution",
"detail": "top contributor authored 67% of commits",
"points": 7.4,
"status": "partial",
"details": [
{
"code": "top_contributor_share",
"params": {
"share": 67
}
}
],
"max_points": 22.5
},
{
"key": "contributor_breadth",
"name": "Contributor breadth",
"detail": "3 contributors",
"points": 4.1,
"status": "partial",
"details": [
{
"code": "contributors_sampled",
"params": {
"count": 3
}
}
],
"max_points": 13.5
},
{
"key": "openssf_scorecard_contributors",
"name": "OpenSSF Scorecard: Contributors",
"detail": "project has 4 contributing companies or organizations",
"points": 10,
"status": "met",
"details": [],
"max_points": 10
}
]
},
{
"key": "responsiveness",
"band": "moderate",
"name": "Issue & PR responsiveness",
"note": "Excluded from scoring (no data or not applicable): Issue resolution. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"issue_resolution"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 68,
"inputs": {
"merged_prs": 83,
"open_issues": 0,
"closed_issues": 0,
"issue_closed_ratio": null,
"closed_unmerged_prs": 5
},
"components": [
{
"key": "issue_resolution",
"name": "Issue resolution",
"detail": "no issues or no data",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_issues_or_data",
"params": {}
}
],
"max_points": 46.75
},
{
"key": "pr_acceptance",
"name": "PR acceptance",
"detail": "83/88 decided PRs merged",
"points": 36.1,
"status": "partial",
"details": [
{
"code": "decided_prs_merged",
"params": {
"merged": 83,
"decided": 88
}
}
],
"max_points": 38.25
},
{
"key": "openssf_scorecard_code_review",
"name": "OpenSSF Scorecard: Code-Review",
"detail": "Found 0/15 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
}
]
},
{
"key": "stewardship",
"band": "moderate",
"name": "Ownership & stewardship",
"note": null,
"notes": [],
"value": 55,
"inputs": {
"followers": 5,
"owner_type": "Organization",
"is_verified": null,
"owner_login": "hihaho",
"public_repos": 9,
"account_age_days": 3245
},
"components": [
{
"key": "ownership_backing",
"name": "Ownership backing",
"detail": "organization-owned",
"points": 30,
"status": "met",
"details": [
{
"code": "owner_organization",
"params": {}
}
],
"max_points": 30
},
{
"key": "verified_domain",
"name": "Verified domain",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 20
},
{
"key": "owner_reach",
"name": "Owner reach",
"detail": "5 followers of hihaho",
"points": 5.6,
"status": "partial",
"details": [
{
"code": "owner_followers",
"params": {
"count": 5,
"login": "hihaho"
}
}
],
"max_points": 25
},
{
"key": "track_record",
"name": "Track record",
"detail": "9 public repos, account ~8 yr old",
"points": 19.3,
"status": "partial",
"details": [
{
"code": "public_repos",
"params": {
"count": 9
}
},
{
"code": "account_age_years",
"params": {
"years": 8
}
}
],
"max_points": 25
}
]
},
{
"key": "package_maintenance",
"band": "excellent",
"name": "Package maintenance",
"note": null,
"notes": [],
"value": 100,
"inputs": {
"packages": [
"hihaho/phpstan-rules"
],
"ecosystems": "packagist",
"any_deprecated": false,
"min_days_since_publish": 21
},
"components": [
{
"key": "published_resolvable",
"name": "Published & resolvable",
"detail": "1 package(s) on packagist",
"points": 25,
"status": "met",
"details": [
{
"code": "packages_published",
"params": {
"count": 1,
"ecosystems": "packagist"
}
}
],
"max_points": 25
},
{
"key": "publish_recency",
"name": "Publish recency",
"detail": "latest publish 21 days ago",
"points": 35,
"status": "met",
"details": [
{
"code": "publish_recency",
"params": {
"days": 21
}
}
],
"max_points": 35
},
{
"key": "version_history",
"name": "Version history",
"detail": "31 published versions",
"points": 20,
"status": "met",
"details": [
{
"code": "published_versions",
"params": {
"count": 31
}
}
],
"max_points": 20
},
{
"key": "not_deprecated",
"name": "Not deprecated",
"detail": "active, not deprecated or yanked",
"points": 20,
"status": "met",
"details": [
{
"code": "package_not_deprecated",
"params": {}
}
],
"max_points": 20
}
]
}
],
"description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
},
{
"key": "engineering",
"band": "moderate",
"name": "Engineering Quality",
"value": 66,
"weight": 0.2,
"metrics": [
{
"key": "engineering_practices",
"band": "good",
"name": "Engineering practices",
"note": null,
"notes": [],
"value": 74,
"inputs": {
"has_ci": true,
"has_tests": true,
"has_editorconfig": true,
"has_linter_config": false,
"has_precommit_config": false
},
"components": [
{
"key": "ci_workflows",
"name": "CI workflows",
"detail": "4 workflow(s)",
"points": 24,
"status": "met",
"details": [
{
"code": "ci_workflows",
"params": {
"count": 4
}
}
],
"max_points": 24
},
{
"key": "tests_present",
"name": "Tests present",
"detail": null,
"points": 24,
"status": "met",
"details": [],
"max_points": 24
},
{
"key": "linter_config",
"name": "Linter config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 16
},
{
"key": "pre_commit_hooks",
"name": "Pre-commit hooks",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 9.6
},
{
"key": "editorconfig",
"name": ".editorconfig",
"detail": null,
"points": 6.4,
"status": "met",
"details": [],
"max_points": 6.4
},
{
"key": "openssf_scorecard_ci_tests",
"name": "OpenSSF Scorecard: CI-Tests",
"detail": "25 out of 25 merged PRs checked by a CI test -- score normalized to 10",
"points": 20,
"status": "met",
"details": [],
"max_points": 20
}
]
},
{
"key": "documentation",
"band": "moderate",
"name": "Documentation",
"note": null,
"notes": [],
"value": 55,
"inputs": {
"topics": [],
"has_wiki": false,
"homepage": "https://guidelines.hihaho.com",
"has_readme": true,
"has_docs_dir": false,
"has_description": true
},
"components": [
{
"key": "readme",
"name": "README",
"detail": null,
"points": 30,
"status": "met",
"details": [],
"max_points": 30
},
{
"key": "documentation_directory",
"name": "Documentation directory",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 25
},
{
"key": "documentation_homepage_site",
"name": "Documentation / homepage site",
"detail": "https://guidelines.hihaho.com",
"points": 15,
"status": "met",
"details": [],
"max_points": 15
},
{
"key": "repository_description",
"name": "Repository description",
"detail": null,
"points": 10,
"status": "met",
"details": [],
"max_points": 10
},
{
"key": "topics",
"name": "Topics",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "wiki",
"name": "Wiki",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
}
]
}
],
"description": "Are baseline engineering and documentation practices in place?"
},
{
"key": "security",
"band": "moderate",
"name": "Security",
"value": 67,
"weight": 0.16,
"metrics": [
{
"key": "security_posture",
"band": "moderate",
"name": "Security posture",
"note": "Excluded from scoring (no data or not applicable): Dangerous-Workflow, Packaging, Pinned-Dependencies, Signed-Releases, Token-Permissions. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"dangerous_workflow",
"packaging",
"pinned_dependencies",
"signed_releases",
"token_permissions"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 67,
"inputs": {
"source": "openssf_scorecard",
"checks_evaluated": 13,
"scorecard_version": "v5.5.0",
"checks_inconclusive": 5,
"scorecard_aggregate": 6.7
},
"components": [
{
"key": "binary_artifacts",
"name": "Binary-Artifacts",
"detail": "no binaries found in the repo",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "branch_protection",
"name": "Branch-Protection",
"detail": "branch protection is not maximal on development and all release branches",
"points": 4.5,
"status": "partial",
"details": [],
"max_points": 7.5
},
{
"key": "ci_tests",
"name": "CI-Tests",
"detail": "25 out of 25 merged PRs checked by a CI test -- score normalized to 10",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "cii_best_practices",
"name": "CII-Best-Practices",
"detail": "no effort to earn an OpenSSF best practices badge detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 2.5
},
{
"key": "code_review",
"name": "Code-Review",
"detail": "Found 0/15 approved changesets -- score normalized to 0",
"points": 0,
"status": "missed",
"details": [],
"max_points": 7.5
},
{
"key": "contributors",
"name": "Contributors",
"detail": "project has 4 contributing companies or organizations",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "dangerous_workflow",
"name": "Dangerous-Workflow",
"detail": "no workflows found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
},
{
"key": "dependency_update_tool",
"name": "Dependency-Update-Tool",
"detail": "update tool detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "fuzzing",
"name": "Fuzzing",
"detail": "project is not fuzzed",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "license",
"name": "License",
"detail": "license file detected",
"points": 2.5,
"status": "met",
"details": [],
"max_points": 2.5
},
{
"key": "maintained",
"name": "Maintained",
"detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
},
{
"key": "packaging",
"name": "Packaging",
"detail": "packaging workflow not detected",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "pinned_dependencies",
"name": "Pinned-Dependencies",
"detail": "no dependencies found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 5
},
{
"key": "sast",
"name": "SAST",
"detail": "SAST tool is run on all commits",
"points": 5,
"status": "met",
"details": [],
"max_points": 5
},
{
"key": "security_policy",
"name": "Security-Policy",
"detail": "security policy file not detected",
"points": 0,
"status": "missed",
"details": [],
"max_points": 5
},
{
"key": "signed_releases",
"name": "Signed-Releases",
"detail": "no releases found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "token_permissions",
"name": "Token-Permissions",
"detail": "No tokens found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 7.5
},
{
"key": "vulnerabilities",
"name": "Vulnerabilities",
"detail": "0 existing vulnerabilities detected",
"points": 7.5,
"status": "met",
"details": [],
"max_points": 7.5
}
]
},
{
"key": "high_risk_jurisdiction_exposure",
"band": "excellent",
"name": "High-Risk Jurisdiction Exposure",
"note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
"notes": [
{
"code": "jurisdiction_evidence_limits",
"params": {}
}
],
"value": 100,
"inputs": {
"meaning": "self-published location evidence; not nationality or citizenship",
"red_flag": false,
"exposures": [],
"policy_countries": [
"Russia",
"Iran",
"North Korea"
],
"review_only_matches": 0,
"assessed_self_published_locations": 7
},
"components": [
{
"key": "policy_exposure_multiplier",
"name": "Policy exposure multiplier",
"detail": "no confirmed policy-scope location match",
"points": 100,
"status": "met",
"details": [
{
"code": "jurisdiction_no_match",
"params": {}
}
],
"max_points": 100
}
]
}
],
"description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
},
{
"key": "ai_readiness",
"band": "moderate",
"name": "AI Readiness",
"value": 54,
"weight": 0,
"metrics": [
{
"key": "ai_agent_context",
"band": "excellent",
"name": "Agent context & guidance",
"note": null,
"notes": [],
"value": 85,
"inputs": {
"has_llms_txt": false,
"legible_history_share": 0.853,
"agent_instruction_files": [
".github/copilot-instructions.md",
"AGENTS.md",
"CLAUDE.md"
],
"agent_instruction_max_bytes": 12530
},
"components": [
{
"key": "agent_instructions",
"name": "Agent instructions",
"detail": ".github/copilot-instructions.md, AGENTS.md, CLAUDE.md",
"points": 45,
"status": "met",
"details": [
{
"code": "file_list",
"params": {
"files": ".github/copilot-instructions.md, AGENTS.md, CLAUDE.md"
}
}
],
"max_points": 45
},
{
"key": "machine_readable_docs_llms_txt",
"name": "Machine-readable docs (llms.txt)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 15
},
{
"key": "legible_commit_history",
"name": "Legible commit history",
"detail": "64 of 75 human commits state their intent (structured subject or explanatory body)",
"points": 40,
"status": "met",
"details": [
{
"code": "legible_history",
"params": {
"legible": 64,
"sampled": 75
}
}
],
"max_points": 40
}
]
},
{
"key": "ai_verify_loop",
"band": "at_risk",
"name": "Verify loop (build / test / typecheck)",
"note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Pinned-Dependencies. Remaining weights renormalized.",
"notes": [
{
"code": "excluded_no_data",
"params": {
"components": [
"openssf_scorecard_pinned_dependencies"
]
}
},
{
"code": "weights_renormalized",
"params": {}
}
],
"value": 44,
"inputs": {
"has_nix": false,
"has_tests": true,
"lockfiles": [],
"has_dockerfile": false,
"typed_language": false,
"bootstrap_files": [],
"has_devcontainer": false,
"has_linter_config": false,
"typecheck_configs": [],
"agent_commit_share": 0.09,
"toolchain_manifests": [],
"dependency_bot_commit_share": 0.08
},
"components": [
{
"key": "one_command_bootstrap",
"name": "One-command bootstrap",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 18
},
{
"key": "automated_tests",
"name": "Automated tests",
"detail": null,
"points": 22,
"status": "met",
"details": [],
"max_points": 22
},
{
"key": "lint_format_config",
"name": "Lint / format config",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "static_type_checking",
"name": "Static type checking",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 11
},
{
"key": "reproducible_environment",
"name": "Reproducible environment",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 10
},
{
"key": "demonstrated_agent_practice",
"name": "Demonstrated agent practice",
"detail": "9 of the last 100 commits agent-authored or agent-credited",
"points": 10,
"status": "met",
"details": [
{
"code": "agent_authored_commits",
"params": {
"count": 9,
"sampled": 100
}
}
],
"max_points": 10
},
{
"key": "automated_maintenance",
"name": "Automated maintenance",
"detail": "8 of the last 100 commits are automated dependency updates",
"points": 8,
"status": "met",
"details": [
{
"code": "dependency_bot_commits",
"params": {
"count": 8,
"sampled": 100
}
}
],
"max_points": 8
},
{
"key": "openssf_scorecard_pinned_dependencies",
"name": "OpenSSF Scorecard: Pinned-Dependencies",
"detail": "no dependencies found",
"points": 0,
"status": "excluded",
"details": [
{
"code": "no_data",
"params": {}
}
],
"max_points": 10
}
]
},
{
"key": "ai_code_legibility",
"band": "moderate",
"name": "Code legibility for models",
"note": null,
"notes": [],
"value": 55,
"inputs": {
"primary_language": "PHP",
"largest_source_bytes": 17604,
"source_files_sampled": 193,
"oversized_source_files": 0
},
"components": [
{
"key": "type_checkable_code",
"name": "Type-checkable code",
"detail": "PHP without a type-check config",
"points": 0,
"status": "missed",
"details": [
{
"code": "no_typecheck_config_language",
"params": {
"language": "PHP"
}
}
],
"max_points": 45
},
{
"key": "manageable_file_sizes",
"name": "Manageable file sizes",
"detail": "0/193 source files over 60KB",
"points": 55,
"status": "met",
"details": [
{
"code": "oversized_source_files",
"params": {
"kb": 60,
"sampled": 193,
"oversized": 0
}
}
],
"max_points": 55
}
]
},
{
"key": "ai_interfaces",
"band": "critical",
"name": "Machine-readable interfaces",
"note": null,
"notes": [],
"value": 20,
"inputs": {
"example_dirs": [],
"has_mcp_signal": true,
"api_schema_files": []
},
"components": [
{
"key": "api_schema_openapi_graphql_proto",
"name": "API schema (OpenAPI/GraphQL/proto)",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 40
},
{
"key": "mcp_server",
"name": "MCP server",
"detail": null,
"points": 20,
"status": "met",
"details": [],
"max_points": 20
},
{
"key": "runnable_examples",
"name": "Runnable examples",
"detail": null,
"points": 0,
"status": "missed",
"details": [],
"max_points": 40
}
]
}
],
"description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
}
],
"metrics_version": "1.13.0"
},
"warnings": [
"Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
"No resolved dependencies carried a version and a supported ecosystem"
],
"report_type": "repository",
"generated_at": "2026-07-28T15:06:50.497816Z",
"schema_version": "0.27.0",
"badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/h/hihaho/phpstan-rules.svg",
"full_name": "hihaho/phpstan-rules",
"license_state": "standard",
"license_spdx": "MIT"
}