chore: install larastan at level 6 with accept-all baseline

Installs larastan/larastan ^3.0 (v3.9.6) as a dev-dependency. Level
6 is the starting target — catches missing typehints, method-
existence, null-safety, and model-property existence. Level 8
deferred to a follow-up sprint after level-6 baseline reaches zero.

Baseline error count at install: 1556 errors across 678 analysed
files (41 distinct identifiers).

Top 10 identifiers (errors / files):
  613 /  87  property.notFound
  289 /  52  missingType.generics
  154 /  31  argument.templateType
   98 /  61  missingType.iterableValue
   77 /  32  argument.type
   50 /  26  method.notFound
   35 /  35  method.childReturnType
   32 /   9  method.unresolvableReturnType
   31 /  10  assign.propertyType
   28 /  17  instanceof.alwaysTrue

Composer scripts:
  - composer analyse              — run static analysis
  - composer analyse:baseline     — regenerate baseline
  - composer analyse:clear-cache  — clear PHPStan result cache

Config deviation from plan: checkGenericClassInNonGenericObjectType
was removed in PHPStan 2.x (which Larastan 3 bundles) — setting
dropped from phpstan.neon, otherwise config matches the work
package verbatim. Defaults cover the original intent.

Documentation: /dev-docs/LARASTAN.md added; CLAUDE.md quality-gates
section introduced (with PHPUnit + Pint + Larastan listed).

Backlog: /dev-docs/BACKLOG.md gets 10 per-identifier reduction
sprints (TECH-LARASTAN-01..10) seeded from the actual baseline top
categories, plus TECH-LARASTAN-CI and TECH-LARASTAN-L8 follow-ups.

Memory limit 2G (baseline generation completed within it).

No production behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 03:46:27 +02:00
parent 198f6f2d3b
commit 7542808cab
8 changed files with 7885 additions and 1 deletions

View File

@@ -12,6 +12,14 @@ Design document: `/dev-docs/design-document.md`
- Frontend: TypeScript, Vue 3 (Composition API), Vuexy/Vuetify, Pinia, TanStack Query
- Testing: PHPUnit (backend), Vitest (frontend)
## Quality gates
- `php artisan test` — PHPUnit feature suite. See Testing rules below.
- `./vendor/bin/pint` — code style. Runs pre-commit in scope; format before merge.
- `composer analyse` — Larastan static analysis at level 6 with
accept-all baseline. New errors beyond the baseline must be fixed
before merge. See `/dev-docs/LARASTAN.md`.
## Repository layout
- `api/` — Laravel backend

1
api/.gitignore vendored
View File

@@ -17,6 +17,7 @@
/public/hot
/public/storage
/storage/*.key
/storage/app/phpstan-tmp
/storage/pail
/vendor
Homestead.json

View File

@@ -19,6 +19,7 @@
},
"require-dev": {
"fakerphp/faker": "^1.23",
"larastan/larastan": "^3.0",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.24",
"laravel/sail": "^1.41",
@@ -55,6 +56,9 @@
"@php artisan config:clear --ansi",
"@php artisan test"
],
"analyse": "vendor/bin/phpstan analyse --memory-limit=2G",
"analyse:baseline": "vendor/bin/phpstan analyse --generate-baseline --memory-limit=2G",
"analyse:clear-cache": "vendor/bin/phpstan clear-result-cache",
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"

186
api/composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "162fa15c2d2ac177259583cf0b271a30",
"content-hash": "4c61afc38c9aea624f684146a9b0719a",
"packages": [
{
"name": "bacon/bacon-qr-code",
@@ -7765,6 +7765,137 @@
},
"time": "2025-04-30T06:54:44+00:00"
},
{
"name": "iamcal/sql-parser",
"version": "v0.7",
"source": {
"type": "git",
"url": "https://github.com/iamcal/SQLParser.git",
"reference": "610392f38de49a44dab08dc1659960a29874c4b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8",
"reference": "610392f38de49a44dab08dc1659960a29874c4b8",
"shasum": ""
},
"require-dev": {
"php-coveralls/php-coveralls": "^1.0",
"phpunit/phpunit": "^5|^6|^7|^8|^9"
},
"type": "library",
"autoload": {
"psr-4": {
"iamcal\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cal Henderson",
"email": "cal@iamcal.com"
}
],
"description": "MySQL schema parser",
"support": {
"issues": "https://github.com/iamcal/SQLParser/issues",
"source": "https://github.com/iamcal/SQLParser/tree/v0.7"
},
"time": "2026-01-28T22:20:33+00:00"
},
{
"name": "larastan/larastan",
"version": "v3.9.6",
"source": {
"type": "git",
"url": "https://github.com/larastan/larastan.git",
"reference": "9ad17e83e96b63536cb6ac39c3d40d29ff9cf636"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/larastan/larastan/zipball/9ad17e83e96b63536cb6ac39c3d40d29ff9cf636",
"reference": "9ad17e83e96b63536cb6ac39c3d40d29ff9cf636",
"shasum": ""
},
"require": {
"ext-json": "*",
"iamcal/sql-parser": "^0.7.0",
"illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
"illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
"php": "^8.2",
"phpstan/phpstan": "^2.1.44"
},
"require-dev": {
"doctrine/coding-standard": "^13",
"laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
"mockery/mockery": "^1.6.12",
"nikic/php-parser": "^5.4",
"orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
"orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
"phpstan/phpstan-deprecation-rules": "^2.0.1",
"phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8"
},
"suggest": {
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
},
"type": "phpstan-extension",
"extra": {
"phpstan": {
"includes": [
"extension.neon"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Larastan\\Larastan\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Can Vural",
"email": "can9119@gmail.com"
}
],
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
"keywords": [
"PHPStan",
"code analyse",
"code analysis",
"larastan",
"laravel",
"package",
"php",
"static analysis"
],
"support": {
"issues": "https://github.com/larastan/larastan/issues",
"source": "https://github.com/larastan/larastan/tree/v3.9.6"
},
"funding": [
{
"url": "https://github.com/canvural",
"type": "github"
}
],
"time": "2026-04-16T10:02:43+00:00"
},
{
"name": "laravel/pail",
"version": "v1.2.4",
@@ -8334,6 +8465,59 @@
},
"time": "2022-02-21T01:04:05+00:00"
},
{
"name": "phpstan/phpstan",
"version": "2.1.51",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc3b523c45e714c70de2ac5113b958223b55dc59",
"reference": "dc3b523c45e714c70de2ac5113b958223b55dc59",
"shasum": ""
},
"require": {
"php": "^7.4|^8.0"
},
"conflict": {
"phpstan/phpstan-shim": "*"
},
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPStan - PHP Static Analysis Tool",
"keywords": [
"dev",
"static analysis"
],
"support": {
"docs": "https://phpstan.org/user-guide/getting-started",
"forum": "https://github.com/phpstan/phpstan/discussions",
"issues": "https://github.com/phpstan/phpstan/issues",
"security": "https://github.com/phpstan/phpstan/security/policy",
"source": "https://github.com/phpstan/phpstan-src"
},
"funding": [
{
"url": "https://github.com/ondrejmirtes",
"type": "github"
},
{
"url": "https://github.com/phpstan",
"type": "github"
}
],
"time": "2026-04-21T18:22:01+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "11.0.12",

7477
api/phpstan-baseline.neon Normal file

File diff suppressed because it is too large Load Diff

19
api/phpstan.neon Normal file
View File

@@ -0,0 +1,19 @@
includes:
- vendor/larastan/larastan/extension.neon
- phpstan-baseline.neon
parameters:
paths:
- app/
- database/
- tests/
level: 6
excludePaths:
- bootstrap/cache/*
- storage/*
- vendor/*
checkModelProperties: true
tmpDir: storage/app/phpstan-tmp

View File

@@ -747,5 +747,144 @@ externe lijsten nog niet compleet zijn.
---
## Larastan reduction sprints
Larastan (PHPStan for Laravel) is geïnstalleerd op level 6 met een
accept-all baseline van 1556 errors over 678 files (41 distinct
identifiers). Zie `/dev-docs/LARASTAN.md` voor werkmodel. Per-categorie
reduction-sprints hieronder — elke sprint mikt op één identifier, laat
de baseline krimpen en regenereert hem aan het einde.
### TECH-LARASTAN-01 — property.notFound
**Priority:** Middel (post-foundation, incremental)
**Scope:** baseline-entries met identifier `property.notFound`.
**Estimate:** 613 errors over 87 files.
**Completion gate:** category count daalt naar 0 in geregenereerde
baseline; volledige test suite groen.
**Approach:**
- Merendeel zit op Eloquent-modellen waar `$user->id` of vergelijkbaar
niet door PHPDoc wordt herkend — los op door `@property` annotaties
op modellen toe te voegen (of via `php artisan ide-helper:models`
als dat acceptabel wordt gevonden).
- Commit per sub-directory als >50 errors.
### TECH-LARASTAN-02 — missingType.generics
**Priority:** Middel
**Scope:** baseline-entries met identifier `missingType.generics`.
**Estimate:** 289 errors over 52 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Zit vooral op factories (`extends Factory<Model>`) en
`HasFactory`-gebruik zonder template. Voeg type-params toe aan
class-declaraties en docblocks.
- Overlapt deels met TYPE_DECLARATION-sprint van Rector.
### TECH-LARASTAN-03 — argument.templateType
**Priority:** Middel
**Scope:** baseline-entries met identifier `argument.templateType`.
**Estimate:** 154 errors over 31 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Voornamelijk `collect(...)` calls waar PHPStan de generieke
template TKey/TValue niet kan resolven. Typeer de input expliciet
of gebruik `Collection::make([...])` met generieke annotatie.
### TECH-LARASTAN-04 — missingType.iterableValue
**Priority:** Middel
**Scope:** baseline-entries met identifier `missingType.iterableValue`.
**Estimate:** 98 errors over 61 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Methode-return-types als `array` zonder value-type. Voeg
`array<string, mixed>` of specifieker toe aan form-requests,
resource `toArray()` methods, factory `definition()` methods.
### TECH-LARASTAN-05 — argument.type
**Priority:** Middel
**Scope:** baseline-entries met identifier `argument.type`.
**Estimate:** 77 errors over 32 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Reële type-mismatches (bijv. string doorgegeven waar `'strict'|'lax'`
vereist is). Case-by-case reviewen — niet mechanisch.
### TECH-LARASTAN-06 — method.notFound
**Priority:** Middel
**Scope:** baseline-entries met identifier `method.notFound`.
**Estimate:** 50 errors over 26 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Meestal "Call to an undefined method Illuminate\\…::users()" —
relationship methods die PHPStan niet kent. Los op via
`@method` annotaties of generieke relationship-return types.
### TECH-LARASTAN-07 — method.childReturnType
**Priority:** Laag
**Scope:** baseline-entries met identifier `method.childReturnType`.
**Estimate:** 35 errors over 35 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Eén-op-één met factory `definition()` methodes. Smeedt samen met
TECH-LARASTAN-02 in één sprint indien praktisch.
### TECH-LARASTAN-08 — method.unresolvableReturnType
**Priority:** Laag
**Scope:** baseline-entries met identifier
`method.unresolvableReturnType`.
**Estimate:** 32 errors over 9 files.
**Completion gate:** category count naar 0; tests groen.
### TECH-LARASTAN-09 — assign.propertyType
**Priority:** Middel (reële type-bug kans hoger dan bij generics)
**Scope:** baseline-entries met identifier `assign.propertyType`.
**Estimate:** 31 errors over 10 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Meestal Carbon vs string mismatch op Eloquent properties —
modelcasts goed zetten zodat Eloquent de datetime teruggeeft waar
hij beloofd is.
### TECH-LARASTAN-10 — instanceof.alwaysTrue
**Priority:** Laag
**Scope:** baseline-entries met identifier `instanceof.alwaysTrue`.
**Estimate:** 28 errors over 17 files.
**Completion gate:** category count naar 0; tests groen.
**Approach:**
- Dead `instanceof`-checks. Prefab voor Rector's `DEAD_CODE`
sprint — wachten of combineren.
### TECH-LARASTAN-CI — CI integration
**Priority:** Middel
**Scope:** wire `composer analyse` als blokkerende PR-gate in CI.
**Depends on:** CI-infrastructuurkeuze.
### TECH-LARASTAN-L8 — level 8 migration
**Priority:** Laag
**Scope:** niveau 6→8 verhogen nadat level-6 baseline op 0 staat.
**Estimate:** onbekend totdat level 6 leeg is.
---
_Laatste update: April 2026_
_Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, DIFF-, APPS-, TECH-, UX-_

52
dev-docs/LARASTAN.md Normal file
View File

@@ -0,0 +1,52 @@
# Larastan (PHPStan for Laravel)
Static analysis for the Crewli backend. Configured at level 6 with
an accept-all baseline.
## Running locally
- `composer analyse` — run static analysis. Suppresses baselined errors.
- `composer analyse:baseline` — regenerate the baseline. Use after a
reduction sprint to lock in the new lower-debt state.
- `composer analyse:clear-cache` — clear PHPStan result cache.
Memory: 2G default. Bump to 4G in `composer.json` if you hit OOM.
## The accept-all baseline model
`phpstan-baseline.neon` is a time-snapshot of every error that existed
when Larastan was first installed. Errors in the baseline are
suppressed; Larastan reports only errors NOT in the baseline.
### Rules
1. **New code must not introduce new errors.** Fix them before merge.
Do NOT regenerate the baseline to absorb new errors.
2. **Refactoring that REMOVES baseline errors is good.** Regenerate
the baseline (`composer analyse:baseline`) so the file reflects
the new lower-debt reality. Commit alongside the refactor.
3. **Reduction sprints REPLACE baseline entries with fixes.** See
`/dev-docs/BACKLOG.md` for sprint items.
## Current target
Level 6. Catches:
- missing typehints
- method existence
- null-safety on common paths
- Laravel model property existence
Level 8 is the eventual target — deferred until level 6 baseline
reaches zero.
## Adding exclusions
Do NOT add blanket `ignoreErrors` patterns to `phpstan.neon`. False
positives get a code-level `@phpstan-ignore-next-line` comment with
upstream-issue justification. Pattern-level ignores rot fast.
## CI integration
Not enabled yet. When added, `composer analyse` is a blocking PR
gate. The "new code must not introduce new errors" rule is only
enforceable with CI.