AI-ready development
Task Recipes
Task Recipes
Copy-paste recipes for common post-generation tasks. Each recipe shows what to change, where to change it, and how to verify.
These recipes are the canonical patterns coding agents should follow when extending a Thunderclap-generated module. They sit on top of Thunderclap Recipes and AI Task Patterns.
Recipe: Add a business action to a generated module
Goal: add an approve action to a generated PurchaseOrder module.
1. Create app/Actions/PurchaseOrder/ApproveAction.php (single-invokable)2. Add a policy method PurchaseOrderPolicy::approve(User, PurchaseOrder)3. Add a route in modules/PurchaseOrder/routes/web.php with can:purchase-order.approve4. Add controller method PurchaseOrderController::approve(...)5. Add Blade button in modules/PurchaseOrder/resources/views/show.blade.php6. Add Pest test for the action class7. Add feature test for the route + policy combinationSee Business actions for the full pattern.
Verify:
php artisan test --filter=ApproveActionphp artisan route:list | grep purchase-orders.approveRecipe: Add a custom permission to a generated module
Goal: give the manager role view-only access to Item.
1. database/seeders/DemoSeeder.php — add permission 'item.view'2. modules/Item/config/item.php — set 'permission' => 'item.view'3. (already wired) routes/web.php applies can:item.view from config4. Assign 'item.view' to the manager role in DemoSeeder5. php artisan db:seed --class=DemoSeederVerify:
php artisan tinker>>> User::where('email','manager@laravolt.dev')->first()->can('item.view')=> trueRecipe: Customize generated form fields
Goal: replace the default text field for price with a masked currency input.
1. Open modules/Item/resources/views/_form.blade.php2. Replace: {!! form()->text('price')->label('Price')->required() !!} with: {!! form()->number('price')->label('Price')->mask('currency')->required() !!}3. Update modules/Item/Requests/Store.php and Update.php: 'price' => ['required', 'numeric', 'min:0']See Forms overview and Input masking.
Recipe: Add a search filter to a generated index
Goal: add a status dropdown filter to the PurchaseOrder index.
1. Open modules/PurchaseOrder/PurchaseOrderTableView.php2. Override filters() and add Dropdown filter3. In data(), apply the filter conditionallyuse Laravolt\Suitable\Filters\Dropdown;public function filters(): array{ return [ Dropdown::make('status')->options([ 'draft' => 'Draft', 'submitted' => 'Submitted', 'approved' => 'Approved', ]), ];}public function data(): Builder{ $query = PurchaseOrder::query() ->autoSort($this->sortPayload()) ->autoSearch(trim($this->search)); if ($this->filterValue('status')) { $query->where('status', $this->filterValue('status')); } return $query->latest();}Recipe: Add a sidebar menu entry without regenerating
Goal: add a menu entry for a hand-built module.
1. Open the module's ServiceProvider2. Override menu() and call app('laravolt.menu.builder')->register(...)3. Pass ->data('permission', 'your.permission') to gate visibilitypublic function menu(): void{ app('laravolt.menu.builder')->register(function ($menu) { if ($menu->modules) { $menu->modules ->add('My Module', route('modules:my-module.index')) ->data('icon', 'sparkles') ->data('permission', 'my-module.view') ->active('modules/my-module/*'); } });}Recipe: Add a browser smoke test for a generated module
Goal: assert the generated index renders and an admin can navigate to create.
// tests/Browser/{Module}/{Module}CrudTest.phpuse App\Models\User;use Database\Seeders\DemoSeeder;beforeEach(function () { $this->seed(DemoSeeder::class); $this->admin = User::query()->where('email', 'admin@laravolt.dev')->firstOrFail();});it('renders index for admin', function () { $this->actingAs($this->admin); visit('/modules/items') ->assertSee('Items') ->assertNoJavaScriptErrors();})->group('browser');Run with pest --group=browser.
Recipe: Re-baseline screenshot snapshots after UI change
php vendor/bin/pest --testsuite Browser --update-snapshotsgit diff --stat tests/.pest/snapshots/git add tests/.pest/snapshots/Always review the diff visually before committing — the snapshot is a hash of the rendered PNG, not the image itself.
Related pages
- AI Context — canonical agent context entry point
- AI Task Patterns — prompt templates
- Thunderclap Recipes — generator-specific patterns
- Business actions — non-CRUD operation convention