• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

marscoin / martianrepublic / 29796968107

21 Jul 2026 02:52AM UTC coverage: 10.965% (-0.03%) from 10.997%
29796968107

push

github

Martian Congress
fix: Drop dynamic DateInterval->w in time_elapsed_string

PHPStan (bumped to larastan 3.10 in the Laravel 12 upgrade) flagged the
$diff->w dynamic property on DateInterval, and its new error-message
format no longer matched the phpstan-baseline entry, failing the Test
Coverage job on both counts. Derive weeks from days in local vars
instead (also removes a PHP 8.2 dynamic-property deprecation) and drop
the now-stale baseline entry. Output unchanged; phpstan clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

0 of 15 new or added lines in 1 file covered. (0.0%)

238 existing lines in 4 files now uncovered.

665 of 6065 relevant lines covered (10.96%)

1.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

18.6
/app/Exceptions/Handler.php
1
<?php
2

3
namespace App\Exceptions;
4

5
use App\Jobs\ErrorTriageJob;
6
use Illuminate\Auth\Access\AuthorizationException;
7
use Illuminate\Auth\AuthenticationException;
8
use Illuminate\Database\Eloquent\ModelNotFoundException;
9
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
10
use Illuminate\Support\Facades\Cache;
11
use Illuminate\Validation\ValidationException;
12
use Livewire\Mechanisms\HandleComponents\CorruptComponentPayloadException;
13
use Symfony\Component\HttpKernel\Exception\HttpException;
14
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
15
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
16
use Throwable;
17

18
class Handler extends ExceptionHandler
19
{
20
    /**
21
     * A list of the exception types that are not reported.
22
     *
23
     * @var array<int, class-string<Throwable>>
24
     */
25
    protected $dontReport = [
26
        //
27
    ];
28

29
    /**
30
     * A list of the inputs that are never flashed for validation exceptions.
31
     *
32
     * @var array<int, string>
33
     */
34
    protected $dontFlash = [
35
        'current_password',
36
        'password',
37
        'password_confirmation',
38
    ];
39

40
    /**
41
     * Register the exception handling callbacks for the application.
42
     */
43
    public function register(): void
145✔
44
    {
45
        $this->reportable(function (Throwable $e) {
145✔
46
            // Sentry (existing)
47
            if (app()->bound('sentry')) {
1✔
UNCOV
48
                app('sentry')->captureException($e);
×
49
            }
50

51
            // AI Error Triage — only for 500-level exceptions
52
            $this->triageError($e);
1✔
53
        });
145✔
54
    }
55

56
    /**
57
     * Dispatch AI triage for server errors, with deduplication.
58
     */
59
    private function triageError(Throwable $e): void
1✔
60
    {
61
        // Skip if no API key configured
62
        if (! config('services.error_triage.openrouter_key')) {
1✔
63
            return;
1✔
64
        }
65

66
        // Skip non-500 exceptions (validation, auth, 404, etc.)
67
        $skipExceptions = [
×
68
            AuthenticationException::class,
×
69
            AuthorizationException::class,
×
70
            ModelNotFoundException::class,
×
71
            ValidationException::class,
×
72
            HttpException::class,
×
73
            NotFoundHttpException::class,
×
74
            MethodNotAllowedHttpException::class,
×
75
            // Client/bot-induced, not a server fault: scrapers POST stale or
76
            // mangled Livewire snapshots to /livewire/update, failing the
77
            // checksum. Steady bot-noise baseline (~2-3/day, always Guest),
78
            // not actionable — don't page on it. Still captured by Sentry.
UNCOV
79
            CorruptComponentPayloadException::class,
×
UNCOV
80
        ];
×
81

UNCOV
82
        foreach ($skipExceptions as $type) {
×
83
            if ($e instanceof $type) {
×
84
                return;
×
85
            }
86
        }
87

88
        // Dedup: same exception class + file + line = same error
UNCOV
89
        $fingerprint = md5(get_class($e).$e->getFile().$e->getLine());
×
UNCOV
90
        $cacheKey = 'error_triage:'.$fingerprint;
×
91
        $cooldown = (int) config('services.error_triage.cooldown_minutes', 15);
×
92

UNCOV
93
        if (Cache::has($cacheKey)) {
×
94
            return;
×
95
        }
96

97
        Cache::put($cacheKey, true, now()->addMinutes($cooldown));
×
98

99
        // Gather request context
100
        $request = request();
×
101
        $url = $request ? $request->fullUrl() : 'CLI';
×
102
        $method = $request ? $request->method() : 'CLI';
×
103
        $userId = $request?->user()?->id;
×
104

105
        ErrorTriageJob::dispatch(
×
106
            exceptionClass: get_class($e),
×
107
            message: $e->getMessage(),
×
108
            file: $e->getFile(),
×
109
            line: $e->getLine(),
×
UNCOV
110
            trace: $e->getTraceAsString(),
×
UNCOV
111
            url: $url,
×
UNCOV
112
            method: $method,
×
UNCOV
113
            userId: $userId ? (string) $userId : null,
×
UNCOV
114
            occurredAt: now()->toDateTimeString(),
×
UNCOV
115
        );
×
116
    }
117
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc