labrodev-exceptionlisted
Install: claude install-skill labrodev/laravel-playbook
# Exceptions: named unhappy paths, placed by origin
Part of the Labrodev playbook. **The law for this component lives in the always-on `labrodev-exception` guideline** (musts, must-nots); the per-file checklist is `rules/exceptions.md`. This skill holds the craft: anatomy, canonical templates, and edge cases.
An Exception is a **named, explicit unhappy path** — not a generic `throw new \Exception('...')`. Every thrown exception in a Labrodev project is its own `final` class, placed by where the failure *originates*, and constructed through a single static `make()` factory so the throw site reads as a sentence.
Placement law (origin, not consumer — the Domain/Shared/Infrastructure/Feature/Layer table) → `labrodev-exception` guideline.
## Template: Domain exception
```php
<?php
declare(strict_types=1);
namespace Core\Domain\Booking\Exceptions;
use Core\Domain\Booking\Models\Booking;
use RuntimeException;
final class BookingOverlapException extends RuntimeException
{
private function __construct(string $message)
{
parent::__construct($message);
}
public static function make(Booking $booking): self
{
return new self(
sprintf('Booking %s overlaps with an existing reservation.', $booking->uuid),
);
}
}
```
Throw site, inside a Domain Action or Service:
```php
if (! BookingRule::canConfirm(booking: $booking)) {
throw BookingOverlapException::make(booking: $booking);
}
```
## Template: Infrastructure excepti