Places where the current code does something you might not expect. Each entry says what happens and what to do about it, so you can plan around it instead of discovering it in production.
The users and widgets screens check view users, delete users, view widgets, edit widgets and delete widgets, but no seeder or migration creates them. Only the super administrator can view a single user, delete users or use the widgets editor; for other roles the widgets screen answers 403 even with manage widgets.
Workaround: create the five permissions on the site (a small migration with Permission::findOrCreate()) and assign them to the roles that need them. See the permissions catalogue.
Core admin routes use auth, verified and 2fa. The Booking, TruCommerce and TruPayments admin groups use web, auth and AdminTheme only, so a user who has not completed two-factor sign-in can open them directly. Your own plugin should use the full stack (Stacks to copy).
Admin routes use the 2fa alias; the public account area and the catch-all route use App\Http\Middleware\TwoFactorMiddleware. Both rely on App\Services\Authenticator. Treat both as active.
BookingCreated, BookingUpdated, BookingStatusChanged, BookingCheckedIn and BookingPaymentStatusChanged exist and are mapped to a listener, but nothing dispatches them. Booking notifications are sent directly by its NotificationService.
Workaround: observe the Booking model's Eloquent events, or listen to the payment events for payment changes. See Events, jobs and schedules.
TicketCloseEvent is fired but has no listener. Add one in a plugin if closing should send a message.
Saved pages contain shortcode tags such as <x-shortcode-testimonials>. When the plugin that provides the tag is disabled, Blade cannot compile the page; the page renderer catches the error and prints the stored HTML unrendered, so every shortcode on that page stops working. Warn site owners to remove a plugin's blocks before deactivating it, and keep shortcode ids stable across versions.
The capability catalogue includes all registered shortcodes; ->ai(['enabled' => false]) removes one. The assistant itself has no settings screen in this release. See AI assistant integration.
A page whose settings contain primary_menu keeps that menu when the site's primary menu changes. Website templates pin menus on many pages. Test menu work on a page without that setting.
config/slugs.php lists Modules\Booking\Models\Service among the models slugs:backfill processes, which is one of the few places the core refers to a module. Booking is protected, so it is always present; add your own models to that list at runtime from your provider.
The list of field types is built fresh for every field, with no registration hook. Compose the existing types, use an ajax-button for actions, or build a custom admin page for richer UI. EncryptedPassword and ProviderModelSelect exist as classes but are not in the list.
Fields stored in .env trigger dotartisan:optimize, which runs config:cache and route:cache. On a development copy this makes new routes and config changes invisible until php artisan optimize:clear.
Labels go through trans(). A label such as 'Pages' or 'Settings' matches a language file and returns an array, and the screen fails with a type error. Use namespaced keys or full sentences.
ThemeOptionStore::put() stores exactly the array you pass for that theme. Merge with persistedForTheme() when you change one value from code.
The underlying package does not support the current Laravel version, so the CDN section is disabled in RegisterSettings; config/cdn.php and the push route remain.
The public site serves a compiled copy of Echo's stylesheet with the site's colours. After npm run build, use Theme options → Advanced → Regenerate Theme CSS.
Library rows are tagged with the theme they were published for. A theme with a different name sees none of Echo's layouts until templates are published for it. See Creating a theme.
Async dashboard widgets are loaded by a separate request that does not pass through AdminTheme. The core admin widgets call Theme::set('admin') in their constructor; do the same in yours if they render theme views.
After installing a plugin from a ZIP: composer dump-autoload, activate, php artisan migrate, run its seeder, and publish its assets. See Package, install and update.
config/translatable.php looks for translation models in App\Models\Translations. Module models must set public string $translationModel = ...::class;.
Core public routes carry a locale prefix for non-default languages; Booking, TruCommerce and TruPayments routes do not. The language still follows the visitor's session.
tests/Pest.php lists only the bundled modules' test folders. Add uses(TestCase::class, RefreshDatabase::class); to your test files. See Testing.
PaymentManager::availableMethods() uses the GeoIP location when it resolves a country and only falls back to the billing address you pass.
QUEUE_CONNECTION defaults to sync; BROADCAST_DRIVER is log with no client library installed. dotartisan:tickets:autoclose exists but is not scheduled.
app/Actions/Auth/ConfirmPassword.php: scaffolding for a package that is not installed.Modules/Booking/routes/api.php: fully commented out and never loaded.vite-module-loader.js at the project root: not used by vite.config.js.eslint.config.js: refers to Vue and TypeScript packages that are not installed; there is no JavaScript lint step.composer ci:check: calls npm run lint:check and npm run types:check, which do not exist in package.json. Use composer test instead.HasConfigModel trait on ticket models toggles string primary keys from config; it applies to the ticket system only.ShortcodeBlog still accepts retired layout attributes so old saved pages keep rendering; they have no effect. The blog has one fixed design by intent.