[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6D1Q5FS406CF3NDX7CBDS23":3,"skill-same:01M6D1Q5FS406CF3NDX7CBDS23":208},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":53,"repositoryHtmlUrl":54,"repositoryHomepageUrl":55,"ownerHtmlUrl":56,"ownerBlogUrl":55,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M6D1Q5FS406CF3NDX7CBDS23","backend-stack-laravel",null,"A Laravel backend (11 to 13): API routes and resource controllers, Form Requests, mass assignment, API Resources, Eloquent without N+1, chunking, transactions and race-free updates, policies and gates, Sanctum, exception rendering to consistent JSON, queues with Horizon, the scheduler, events and listeners, caching, config caching and Octane in production, tests with Pest or PHPUnit, Larastan and Pint. Read when building or changing a Laravel app.","enowdev\u002Fenowxcli","enowdev","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F179420349?v=4","01M6D1MVX2KC0X3Z9NESE7P22K","Enow Dev.","01M6D1MW1FMZGBDKWAJY4GQX81",67,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fbackend-stack-laravel\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"stack","laravel","backend","backend stack laravel","config","scheduler","Production","api","transactions","json","read","Sanctum","caching","chunking","gates","tests","2026-11-10 10:12:30",false,"---\nname: backend-stack-laravel\ndescription: \"A Laravel backend (11 to 13): API routes and resource controllers, Form Requests, mass assignment, API Resources, Eloquent without N+1, chunking, transactions and race-free updates, policies and gates, Sanctum, exception rendering to consistent JSON, queues with Horizon, the scheduler, events and listeners, caching, config caching and Octane in production, tests with Pest or PHPUnit, Larastan and Pint. Read when building or changing a Laravel app.\"\n---\n\n# Laravel\n\nThe generated Laravel app: `$request->all()` into `Order::create()`,\n`$guarded = []`, a model returned as JSON with hidden columns and a lazy load\nper row, authorisation only in the Vue page, `env()` called in a controller\n(it returns `null` once config is cached), a job dispatched inside a\ntransaction that runs before the commit, and a scheduled command running on\nevery server. The backend rules are in the `backend` skill; these are\nLaravel's own. Keep the project's version, structure and packages; the notes\nbelow assume Laravel 11 or later (`bootstrap\u002Fapp.php` for middleware and\nexceptions). Laravel 13 (current, PHP 8.3+) adds PHP attributes for much of\nthe configuration; use them when the project does.\n\n## 1. Routes and controllers\n\n- API routes in `routes\u002Fapi.php` (`php artisan install:api` adds it), grouped\n  with a prefix and middleware (`auth:sanctum`); resource controllers\n  (`Route::apiResource('orders', OrderController::class)`) and route model\n  binding.\n- Thin controllers: a Form Request in, one call to an action or service\n  class (`app\u002FActions\u002FPayOrder.php`) that holds the rules, an API Resource\n  out.\n- Nested resources with `->scoped()`, so `\u002Forders\u002F1\u002Fitems\u002F5` is a `404` when\n  item 5 is not order 1's.\n- The base controller has no traits since Laravel 11: `$this->authorize()`\n  needs `AuthorizesRequests` added; `Gate::authorize('refund', $order)` works\n  anywhere (Laravel 13 also has the `#[Authorize]` controller attribute).\n- Rate limits defined in `AppServiceProvider::boot()`, applied as\n  `throttle:api`:\n  `RateLimiter::for('api', fn (Request $r) => Limit::perMinute(60)->by($r->user()?->id ?: $r->ip()))`;\n  tighter ones (`Limit::perMinute(5)`) on sign-in and anything that mails.\n\n## 2. Input\n\n- A Form Request per write (`StoreOrderRequest`) with `rules()` and\n  `authorize()`; the controller uses `$request->validated()`, never\n  `$request->all()`.\n- Models list `$fillable` explicitly; never `$guarded = []`. Fields a user\n  must not set (`role`, `price`, `user_id`) are assigned in code. Laravel 13\n  writes the list as `#[Fillable([...])]` on the class.\n- Unknown fields refused (`backend-api`): Laravel 13 has\n  `#[FailOnUnknownFields]` per request, or `FormRequest::failOnUnknownFields()`\n  once in a service provider.\n\n```php\npublic function rules(): array\n{\n    return [\n        'lines' => ['required', 'array', 'min:1', 'max:50'],\n        'lines.*.product_id' => ['required', 'uuid', Rule::exists('products', 'id')],\n        'lines.*.quantity' => ['required', 'integer', 'between:1,100'],\n        'note' => ['nullable', 'string', 'max:500'],\n    ];\n}\n```\n\n## 3. Output\n\n- API Resources (`OrderResource`, `OrderResource::collection`) decide the\n  fields; never return a model or `->toArray()` directly (hidden columns and\n  future ones leak).\n- Lists with `->paginate(25)` or `->cursorPaginate(25)` in the query,\n  `with()` for the relations the resource shows.\n- Relations in a resource through `$this->whenLoaded('items')` and counts\n  through `whenCounted`, so a resource never triggers a query of its own.\n  `JsonResource::withoutWrapping()` in `AppServiceProvider` returns a record\n  as itself rather than inside `data`.\n- Errors in the format of `backend-errors`, rendered in `bootstrap\u002Fapp.php`\n  with `->withExceptions(...)` for requests that expect JSON; `abort(404)` and\n  domain exceptions with a `render` method rather than hand-built responses.\n\n```php\n->withExceptions(function (Exceptions $exceptions): void {\n    $exceptions->shouldRenderJsonWhen(fn (Request $r) => $r->is('api\u002F*') || $r->expectsJson());\n    $exceptions->render(function (ValidationException $e, Request $request) {\n        if (! $request->is('api\u002F*')) {\n            return null; \u002F\u002F web forms keep the redirect back with errors\n        }\n        $errors = [];\n        foreach ($e->validator->failed() as $field => $rules) { \u002F\u002F ['lines.0.quantity' => ['Between' => [...]]]\n            foreach (array_keys($rules) as $rule) {\n                $errors[] = ['field' => preg_replace('\u002F\\.(\\d+)\u002F', '[$1]', $field), 'code' => Str::snake($rule)];\n            }\n        }\n        return response()->json(['type' => 'https:\u002F\u002Fexample.com\u002Ferrors\u002Fvalidation-failed', 'status' => 422,\n            'code' => 'validation_failed', 'errors' => $errors], 422, ['Content-Type' => 'application\u002Fproblem+json']);\n    });\n})\n```\n\n- Laravel answers validation with `422`; keep it when clients rely on it\n  (`backend-api` allows either).\n\n## 4. Who may do what\n\n- Policies per model (`OrderPolicy`) and `$this->authorize('refund',\n  $order)` or `Gate`, on every action; queries scoped to the user or tenant\n  (a global scope, or `$user->orders()`), so another id in the URL gives\n  `404`.\n- A policy can answer `Response::denyAsNotFound()` where even a `403` would\n  reveal that the record exists; `before()` for the admin shortcut.\n\n```php\nclass OrderPolicy\n{\n    public function refund(User $user, Order $order): Response\n    {\n        return $order->user_id === $user->id ? Response::allow() : Response::denyAsNotFound();\n    }\n}\n\n\u002F\u002F The controller: a Form Request in, the check, one action, a resource out.\npublic function refund(RefundOrderRequest $request, Order $order, RefundOrder $refund): OrderResource\n{\n    Gate::authorize('refund', $order);\n    return new OrderResource($refund($order, $request->validated('reason')));\n}\n```\n\n- Sanctum: cookie sessions for the app's own frontend (with `statefulApi()`\n  and CSRF), personal access tokens with abilities for mobile and other\n  clients. Starter kits (Breeze, or the React, Vue and Livewire kits) and\n  Fortify for sign-in, reset and verification rather than writing them.\n- Sanctum tokens never expire by default (`expiration` is `null` in\n  `config\u002Fsanctum.php`): set it, and schedule `sanctum:prune-expired\n  --hours=24`. Abilities are checked with `$request->user()->tokenCan(...)`\n  or the `abilities` and `ability` middleware, whose aliases are registered\n  in `bootstrap\u002Fapp.php`.\n\n## 5. Eloquent and data\n\n- `Model::preventLazyLoading(! app()->isProduction())` in\n  `AppServiceProvider`, so N+1 fails in development; `with()`,\n  `withCount()`, `load()` to load relations. `Model::shouldBeStrict()` also\n  makes a silently discarded or a missing attribute throw.\n- `DB::transaction(fn () => ...)` around writes that belong together.\n  Race-free stock with a conditional update that reports rows changed:\n  `Product::whereKey($id)->where('stock', '>=', $qty)->decrement('stock',\n  $qty)` returns 0 when there is not enough; `lockForUpdate()` inside the\n  transaction when you must read then write.\n- `DB::transaction($callback, 3)` retries a deadlocked transaction up to three\n  times; `createOrFirst()` leans on a unique index where `firstOrCreate()`\n  races; `upsert()` for bulk writes.\n- Casts for types (`'total' => 'integer'`, enums, `'immutable_datetime'`) in\n  the `casts()` method, money as integers, timestamps in UTC (`'timezone' =>\n  'UTC'` in `config\u002Fapp.php`; `APP_TIMEZONE=UTC` in Laravel 11), converted\n  for display.\n- Large sets in pieces: `chunkById(500, ...)` (plain `chunk()` skips rows\n  when the callback changes the filtered column), `lazyById()` to stream,\n  never `->get()` on a whole table.\n- Public ids with `HasUuids` (UUIDv7 since Laravel 12, so inserts stay in\n  index order) or `HasUlids`; `php artisan model:show Order` lists a model's\n  casts, relations and columns.\n- Migrations from `php artisan make:migration`, with foreign keys\n  (`foreignId('order_id')->constrained()->restrictOnDelete()`) and unique\n  indexes; factories and seeders for sample data.\n\n## 6. Queues and the scheduler\n\n- Jobs implementing `ShouldQueue` with `$tries`, `backoff()`, `ShouldBeUnique`\n  where a duplicate would harm, and `afterCommit` so they never run before\n  the transaction; the database driver to start, Redis with Horizon later.\n- Laravel 13 declares these with attributes (`#[Tries(5)]`,\n  `#[Backoff(10, 60, 300)]`, `#[Timeout(120)]`); `ShouldQueueAfterCommit`\n  makes every dispatch wait for the commit.\n\n```php\n#[Tries(5), Backoff(10, 60, 300), Timeout(60)]\nclass SendReceipt implements ShouldQueueAfterCommit\n{\n    use Queueable;\n\n    public function __construct(public int $orderId) {} \u002F\u002F an id, fetched again in handle()\n\n    public function middleware(): array\n    {\n        return [new WithoutOverlapping($this->orderId)];\n    }\n\n    public function handle(ReceiptMailer $mailer): void\n    {\n        $mailer->sendOnce(Order::findOrFail($this->orderId)); \u002F\u002F safe to run twice\n    }\n}\n```\n\n- A job's timeout stays below the connection's `retry_after` (90 seconds in\n  `config\u002Fqueue.php`), or a slow job is handed to a second worker while the\n  first still runs. Job middleware for the rest: `WithoutOverlapping`,\n  `RateLimited`, `ThrottlesExceptions`; a `failed()` method for the last\n  failure (`backend-jobs`).\n- Horizon supervises Redis workers from `config\u002Fhorizon.php`; each deploy\n  runs `php artisan horizon:terminate` (plain workers: `queue:restart`, and\n  `queue:work --max-time=3600`), since long-lived workers keep the old code.\n- Scheduled work in `routes\u002Fconsole.php` with `->onOneServer()` and\n  `->withoutOverlapping()`; one `schedule:run` cron or `schedule:work`.\n  `onOneServer` needs a shared cache store with locks (Redis, database,\n  Memcached, DynamoDB).\n- Mail and notifications queued (`ShouldQueue`), with texts through the lang\n  files (the `i18n` skill).\n\n## 7. Events and caching\n\n- Events for side effects other parts react to; listeners in\n  `app\u002FListeners` are discovered by their type-hint, queued with\n  `ShouldQueue`, and wait for the commit with\n  `ShouldHandleEventsAfterCommit`.\n- `Cache::remember(\"product:{$id}\", now()->addMinutes(10), fn () => ...)`;\n  `Cache::flexible('stats', [300, 600], fn () => ...)` serves stale while it\n  refreshes; `Cache::lock(\"order:{$id}\", 10)->block(5, fn () => ...)` for one\n  writer at a time; tags only on Redis and Memcached. More in\n  `backend-caching`.\n\n## 8. Config, logs and production\n\n- `env()` only inside `config\u002F*.php`; code reads `config('services.x.key')`,\n  and production runs `php artisan config:cache`.\n- `Log::withContext(['request_id' => ...])` in a middleware; no request\n  bodies with passwords or tokens in the log. `Context::add('request_id',\n  $id)` (Laravel 11+) does more: it lands on every log record and travels\n  into queued jobs.\n- Deploys run `php artisan optimize` (config, events, routes and views\n  cached), migrations as their own step, and the worker restart above.\n  `APP_DEBUG=false`; JSON logs to stderr in containers (`LOG_CHANNEL=stderr`,\n  `LOG_STDERR_FORMATTER=Monolog\\Formatter\\JsonFormatter`); the `\u002Fup` route\n  for health checks (`backend-observability`).\n- Octane (FrankenPHP, Swoole or RoadRunner) boots the app once for many\n  requests: no request or user state in singletons or static properties\n  (resolve the request where it is used), `octane:reload` on deploy, and\n  `--max-requests` (500 by default) recycles workers that leak.\n\n## 9. Tests\n\nPest or PHPUnit feature tests with `RefreshDatabase`, factories and\n`actingAs($user)`; `postJson()` with `assertStatus`, `assertJsonPath` and\n`assertJsonValidationErrors`; `Http::fake()`, `Queue::fake()`, `Mail::fake()`\nand `$this->travelTo(...)`; the test database of the same kind as\nproduction (`backend-testing`).\n\n```php\nit(\"does not reveal another user's order\", function () {\n    $order = Order::factory()->create(); \u002F\u002F belongs to someone else\n    $this->actingAs(User::factory()->create())\n        ->postJson(\"\u002Fapi\u002Forders\u002F{$order->id}\u002Fpay\")\n        ->assertNotFound();\n    expect($order->fresh()->status)->toBe('open');\n});\n```\n\n- `Http::preventStrayRequests()` fails any outbound call without a fake;\n  `Sanctum::actingAs($user, ['orders:write'])` for token abilities;\n  `Exceptions::fake()` to assert what was reported; `$this->freezeTime()`;\n  `php artisan test --parallel` gives each process its own database.\n- `assertJsonValidationErrors` reads Laravel's default `errors` object: once\n  validation renders as problem details, assert with `assertJsonPath` on the\n  `errors` list instead.\n- Pest 5 needs PHP 8.4; Pest 4 runs on PHP 8.3.\n\n## 10. Check\n\n`php artisan test`, `.\u002Fvendor\u002Fbin\u002Fpint --test`, Larastan or PHPStan when the\nproject has it (`vendor\u002Fbin\u002Fphpstan analyse`, with Larastan's\n`extension.neon` included and the level raised over time), `php artisan\nroute:list` for the routes, `composer audit`, then call them with `curl`.\n\n## Avoid\n\n`$request->all()` into `create()`; `$guarded = []`; models returned as JSON;\nauthorisation only in the frontend; lazy loading in a loop; `env()` outside\nconfig; a job that runs before its transaction commits; scheduled commands\nthat run on every server; rules written in controllers; a job timeout above\n`retry_after`; workers left on old code after a deploy; Sanctum tokens that\nnever expire; `chunk()` over rows the callback updates; request state in a\nsingleton under Octane.\n","# Laravel\n\nThe generated Laravel app: `$request->all()` into `Order::create()`,\n`$guarded = []`, a model returned as JSON with hidden columns and a lazy load\nper row, authorisation only in the Vue page, `env()` called in a controller\n(it returns `null` once config is cached), a job dispatched inside a\ntransaction that runs before the commit, and a scheduled command running on\nevery server. The backend rules are in the `backend` skill; these are\nLaravel's own. Keep the project's version, structure and packages; the notes\nbelow assume Laravel 11 or later (`bootstrap\u002Fapp.php` for middleware and\nexceptions). Laravel 13 (current, PHP 8.3+) adds PHP attributes for much of\nthe configuration; use them when the project does.\n\n## 1. Routes and controllers\n\n- API routes in `routes\u002Fapi.php` (`php artisan install:api` adds it), grouped\n  with a prefix and middleware (`auth:sanctum`); resource controllers\n  (`Route::apiResource('orders', OrderController::class)`) and route model\n  binding.\n- Thin controllers: a Form Request in, one call to an action or service\n  class (`app\u002FActions\u002FPayOrder.php`) that holds the rules, an API Resource\n  out.\n- Nested resources with `->scoped()`, so `\u002Forders\u002F1\u002Fitems\u002F5` is a `404` when\n  item 5 is not order 1's.\n- The base controller has no traits since Laravel 11: `$this->authorize()`\n  needs `AuthorizesRequests` added; `Gate::authorize('refund', $order)` works\n  anywhere (Laravel 13 also has the `#[Authorize]` controller attribute).\n- Rate limits defined in `AppServiceProvider::boot()`, applied as\n  `throttle:api`:\n  `RateLimiter::for('api', fn (Request $r) => Limit::perMinute(60)->by($r->user()?->id ?: $r->ip()))`;\n  tighter ones (`Limit::perMinute(5)`) on sign-in and anything that mails.\n\n## 2. Input\n\n- A Form Request per write (`StoreOrderRequest`) with `rules()` and\n  `authorize()`; the controller uses `$request->validated()`, never\n  `$request->all()`.\n- Models list `$fillable` explicitly; never `$guarded = []`. Fields a user\n  must not set (`role`, `price`, `user_id`) are assigned in code. Laravel 13\n  writes the list as `#[Fillable([...])]` on the class.\n- Unknown fields refused (`backend-api`): Laravel 13 has\n  `#[FailOnUnknownFields]` per request, or `FormRequest::failOnUnknownFields()`\n  once in a service provider.\n\n```php\npublic function rules(): array\n{\n    return [\n        'lines' => ['required', 'array', 'min:1', 'max:50'],\n        'lines.*.product_id' => ['required', 'uuid', Rule::exists('products', 'id')],\n        'lines.*.quantity' => ['required', 'integer', 'between:1,100'],\n        'note' => ['nullable', 'string', 'max:500'],\n    ];\n}\n```\n\n## 3. Output\n\n- API Resources (`OrderResource`, `OrderResource::collection`) decide the\n  fields; never return a model or `->toArray()` directly (hidden columns and\n  future ones leak).\n- Lists with `->paginate(25)` or `->cursorPaginate(25)` in the query,\n  `with()` for the relations the resource shows.\n- Relations in a resource through `$this->whenLoaded('items')` and counts\n  through `whenCounted`, so a resource never triggers a query of its own.\n  `JsonResource::withoutWrapping()` in `AppServiceProvider` returns a record\n  as itself rather than inside `data`.\n- Errors in the format of `backend-errors`, rendered in `bootstrap\u002Fapp.php`\n  with `->withExceptions(...)` for requests that expect JSON; `abort(404)` and\n  domain exceptions with a `render` method rather than hand-built responses.\n\n```php\n->withExceptions(function (Exceptions $exceptions): void {\n    $exceptions->shouldRenderJsonWhen(fn (Request $r) => $r->is('api\u002F*') || $r->expectsJson());\n    $exceptions->render(function (ValidationException $e, Request $request) {\n        if (! $request->is('api\u002F*')) {\n            return null; \u002F\u002F web forms keep the redirect back with errors\n        }\n        $errors = [];\n        foreach ($e->validator->failed() as $field => $rules) { \u002F\u002F ['lines.0.quantity' => ['Between' => [...]]]\n            foreach (array_keys($rules) as $rule) {\n                $errors[] = ['field' => preg_replace('\u002F\\.(\\d+)\u002F', '[$1]', $field), 'code' => Str::snake($rule)];\n            }\n        }\n        return response()->json(['type' => 'https:\u002F\u002Fexample.com\u002Ferrors\u002Fvalidation-failed', 'status' => 422,\n            'code' => 'validation_failed', 'errors' => $errors], 422, ['Content-Type' => 'application\u002Fproblem+json']);\n    });\n})\n```\n\n- Laravel answers validation with `422`; keep it when clients rely on it\n  (`backend-api` allows either).\n\n## 4. Who may do what\n\n- Policies per model (`OrderPolicy`) and `$this->authorize('refund',\n  $order)` or `Gate`, on every action; queries scoped to the user or tenant\n  (a global scope, or `$user->orders()`), so another id in the URL gives\n  `404`.\n- A policy can answer `Response::denyAsNotFound()` where even a `403` would\n  reveal that the record exists; `before()` for the admin shortcut.\n\n```php\nclass OrderPolicy\n{\n    public function refund(User $user, Order $order): Response\n    {\n        return $order->user_id === $user->id ? Response::allow() : Response::denyAsNotFound();\n    }\n}\n\n\u002F\u002F The controller: a Form Request in, the check, one action, a resource out.\npublic function refund(RefundOrderRequest $request, Order $order, RefundOrder $refund): OrderResource\n{\n    Gate::authorize('refund', $order);\n    return new OrderResource($refund($order, $request->validated('reason')));\n}\n```\n\n- Sanctum: cookie sessions for the app's own frontend (with `statefulApi()`\n  and CSRF), personal access tokens with abilities for mobile and other\n  clients. Starter kits (Breeze, or the React, Vue and Livewire kits) and\n  Fortify for sign-in, reset and verification rather than writing them.\n- Sanctum tokens never expire by default (`expiration` is `null` in\n  `config\u002Fsanctum.php`): set it, and schedule `sanctum:prune-expired\n  --hours=24`. Abilities are checked with `$request->user()->tokenCan(...)`\n  or the `abilities` and `ability` middleware, whose aliases are registered\n  in `bootstrap\u002Fapp.php`.\n\n## 5. Eloquent and data\n\n- `Model::preventLazyLoading(! app()->isProduction())` in\n  `AppServiceProvider`, so N+1 fails in development; `with()`,\n  `withCount()`, `load()` to load relations. `Model::shouldBeStrict()` also\n  makes a silently discarded or a missing attribute throw.\n- `DB::transaction(fn () => ...)` around writes that belong together.\n  Race-free stock with a conditional update that reports rows changed:\n  `Product::whereKey($id)->where('stock', '>=', $qty)->decrement('stock',\n  $qty)` returns 0 when there is not enough; `lockForUpdate()` inside the\n  transaction when you must read then write.\n- `DB::transaction($callback, 3)` retries a deadlocked transaction up to three\n  times; `createOrFirst()` leans on a unique index where `firstOrCreate()`\n  races; `upsert()` for bulk writes.\n- Casts for types (`'total' => 'integer'`, enums, `'immutable_datetime'`) in\n  the `casts()` method, money as integers, timestamps in UTC (`'timezone' =>\n  'UTC'` in `config\u002Fapp.php`; `APP_TIMEZONE=UTC` in Laravel 11), converted\n  for display.\n- Large sets in pieces: `chunkById(500, ...)` (plain `chunk()` skips rows\n  when the callback changes the filtered column), `lazyById()` to stream,\n  never `->get()` on a whole table.\n- Public ids with `HasUuids` (UUIDv7 since Laravel 12, so inserts stay in\n  index order) or `HasUlids`; `php artisan model:show Order` lists a model's\n  casts, relations and columns.\n- Migrations from `php artisan make:migration`, with foreign keys\n  (`foreignId('order_id')->constrained()->restrictOnDelete()`) and unique\n  indexes; factories and seeders for sample data.\n\n## 6. Queues and the scheduler\n\n- Jobs implementing `ShouldQueue` with `$tries`, `backoff()`, `ShouldBeUnique`\n  where a duplicate would harm, and `afterCommit` so they never run before\n  the transaction; the database driver to start, Redis with Horizon later.\n- Laravel 13 declares these with attributes (`#[Tries(5)]`,\n  `#[Backoff(10, 60, 300)]`, `#[Timeout(120)]`); `ShouldQueueAfterCommit`\n  makes every dispatch wait for the commit.\n\n```php\n#[Tries(5), Backoff(10, 60, 300), Timeout(60)]\nclass SendReceipt implements ShouldQueueAfterCommit\n{\n    use Queueable;\n\n    public function __construct(public int $orderId) {} \u002F\u002F an id, fetched again in handle()\n\n    public function middleware(): array\n    {\n        return [new WithoutOverlapping($this->orderId)];\n    }\n\n    public function handle(ReceiptMailer $mailer): void\n    {\n        $mailer->sendOnce(Order::findOrFail($this->orderId)); \u002F\u002F safe to run twice\n    }\n}\n```\n\n- A job's timeout stays below the connection's `retry_after` (90 seconds in\n  `config\u002Fqueue.php`), or a slow job is handed to a second worker while the\n  first still runs. Job middleware for the rest: `WithoutOverlapping`,\n  `RateLimited`, `ThrottlesExceptions`; a `failed()` method for the last\n  failure (`backend-jobs`).\n- Horizon supervises Redis workers from `config\u002Fhorizon.php`; each deploy\n  runs `php artisan horizon:terminate` (plain workers: `queue:restart`, and\n  `queue:work --max-time=3600`), since long-lived workers keep the old code.\n- Scheduled work in `routes\u002Fconsole.php` with `->onOneServer()` and\n  `->withoutOverlapping()`; one `schedule:run` cron or `schedule:work`.\n  `onOneServer` needs a shared cache store with locks (Redis, database,\n  Memcached, DynamoDB).\n- Mail and notifications queued (`ShouldQueue`), with texts through the lang\n  files (the `i18n` skill).\n\n## 7. Events and caching\n\n- Events for side effects other parts react to; listeners in\n  `app\u002FListeners` are discovered by their type-hint, queued with\n  `ShouldQueue`, and wait for the commit with\n  `ShouldHandleEventsAfterCommit`.\n- `Cache::remember(\"product:{$id}\", now()->addMinutes(10), fn () => ...)`;\n  `Cache::flexible('stats', [300, 600], fn () => ...)` serves stale while it\n  refreshes; `Cache::lock(\"order:{$id}\", 10)->block(5, fn () => ...)` for one\n  writer at a time; tags only on Redis and Memcached. More in\n  `backend-caching`.\n\n## 8. Config, logs and production\n\n- `env()` only inside `config\u002F*.php`; code reads `config('services.x.key')`,\n  and production runs `php artisan config:cache`.\n- `Log::withContext(['request_id' => ...])` in a middleware; no request\n  bodies with passwords or tokens in the log. `Context::add('request_id',\n  $id)` (Laravel 11+) does more: it lands on every log record and travels\n  into queued jobs.\n- Deploys run `php artisan optimize` (config, events, routes and views\n  cached), migrations as their own step, and the worker restart above.\n  `APP_DEBUG=false`; JSON logs to stderr in containers (`LOG_CHANNEL=stderr`,\n  `LOG_STDERR_FORMATTER=Monolog\\Formatter\\JsonFormatter`); the `\u002Fup` route\n  for health checks (`backend-observability`).\n- Octane (FrankenPHP, Swoole or RoadRunner) boots the app once for many\n  requests: no request or user state in singletons or static properties\n  (resolve the request where it is used), `octane:reload` on deploy, and\n  `--max-requests` (500 by default) recycles workers that leak.\n\n## 9. Tests\n\nPest or PHPUnit feature tests with `RefreshDatabase`, factories and\n`actingAs($user)`; `postJson()` with `assertStatus`, `assertJsonPath` and\n`assertJsonValidationErrors`; `Http::fake()`, `Queue::fake()`, `Mail::fake()`\nand `$this->travelTo(...)`; the test database of the same kind as\nproduction (`backend-testing`).\n\n```php\nit(\"does not reveal another user's order\", function () {\n    $order = Order::factory()->create(); \u002F\u002F belongs to someone else\n    $this->actingAs(User::factory()->create())\n        ->postJson(\"\u002Fapi\u002Forders\u002F{$order->id}\u002Fpay\")\n        ->assertNotFound();\n    expect($order->fresh()->status)->toBe('open');\n});\n```\n\n- `Http::preventStrayRequests()` fails any outbound call without a fake;\n  `Sanctum::actingAs($user, ['orders:write'])` for token abilities;\n  `Exceptions::fake()` to assert what was reported; `$this->freezeTime()`;\n  `php artisan test --parallel` gives each process its own database.\n- `assertJsonValidationErrors` reads Laravel's default `errors` object: once\n  validation renders as problem details, assert with `assertJsonPath` on the\n  `errors` list instead.\n- Pest 5 needs PHP 8.4; Pest 4 runs on PHP 8.3.\n\n## 10. Check\n\n`php artisan test`, `.\u002Fvendor\u002Fbin\u002Fpint --test`, Larastan or PHPStan when the\nproject has it (`vendor\u002Fbin\u002Fphpstan analyse`, with Larastan's\n`extension.neon` included and the level raised over time), `php artisan\nroute:list` for the routes, `composer audit`, then call them with `curl`.\n\n## Avoid\n\n`$request->all()` into `create()`; `$guarded = []`; models returned as JSON;\nauthorisation only in the frontend; lazy loading in a loop; `env()` outside\nconfig; a job that runs before its transaction commits; scheduled commands\nthat run on every server; rules written in controllers; a job timeout above\n`retry_after`; workers left on old code after a deploy; Sanctum tokens that\nnever expire; `chunk()` over rows the callback updates; request state in a\nsingleton under Octane.\n",{"name":5,"description":7},"main","38293fe1b096e6bf3646c7344c38786ed688f464","crates\u002Fenowx-core\u002Fskills\u002Fbackend-stack-laravel",21,"https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli","https:\u002F\u002Fenowx.ai","https:\u002F\u002Fgithub.com\u002Fenowdev","Pretending to be a Developer","2026-10-06 02:05:35",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add enowdev\u002Fenowxcli --skill backend-stack-laravel",{"tool":65,"command":66},"bunx","bunx skills add enowdev\u002Fenowxcli --skill backend-stack-laravel",{"tool":68,"command":69},"pnpm","pnpm dlx skills add enowdev\u002Fenowxcli --skill backend-stack-laravel","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fbackend-stack-laravel\u002FSKILL.md",true,[73,79,83,87,91,95,99,103,107,111,115,119,123,129,133,137,141,145,149,153,157,161,165,169,173,177,181,185,189,192,196,200,204],{"path":74,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":77,"sourceHtmlUrl":78},"references\u002Fagentic-rft.md","REFERENCE","参考文档",3234,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fagentic-rft.md",{"path":80,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":81,"sourceHtmlUrl":82},"references\u002Fdataset-formats.md",3740,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fdataset-formats.md",{"path":84,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":85,"sourceHtmlUrl":86},"references\u002Fdeployment.md",3503,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fdeployment.md",{"path":88,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":89,"sourceHtmlUrl":90},"references\u002Fevaluation.md",5924,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fevaluation.md",{"path":92,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":93,"sourceHtmlUrl":94},"references\u002Fgrader-design.md",3437,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fgrader-design.md",{"path":96,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":97,"sourceHtmlUrl":98},"references\u002Fhyperparameters.md",3233,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fhyperparameters.md",{"path":100,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":101,"sourceHtmlUrl":102},"references\u002Flarge-file-uploads.md",927,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Flarge-file-uploads.md",{"path":104,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":105,"sourceHtmlUrl":106},"references\u002Fplatform-gotchas.md",2069,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fplatform-gotchas.md",{"path":108,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":109,"sourceHtmlUrl":110},"references\u002Freward-hacking.md",2945,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Freward-hacking.md",{"path":112,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":113,"sourceHtmlUrl":114},"references\u002Ftraining-curves.md",4183,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Ftraining-curves.md",{"path":116,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":117,"sourceHtmlUrl":118},"references\u002Ftraining-types.md",3161,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Ftraining-types.md",{"path":120,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":121,"sourceHtmlUrl":122},"references\u002Fvision-fine-tuning.md",4205,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Freferences\u002Fvision-fine-tuning.md",{"path":124,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":127,"sourceHtmlUrl":128},"scripts\u002Fcalibrate_grader.py","SCRIPT","脚本",9493,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fcalibrate_grader.py",{"path":130,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":131,"sourceHtmlUrl":132},"scripts\u002Fcheck_training.py",7987,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fcheck_training.py",{"path":134,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":135,"sourceHtmlUrl":136},"scripts\u002Fcleanup.py",9687,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fcleanup.py",{"path":138,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":139,"sourceHtmlUrl":140},"scripts\u002Fcommon.py",7808,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fcommon.py",{"path":142,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":143,"sourceHtmlUrl":144},"scripts\u002Fconvert_dataset.py",10936,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fconvert_dataset.py",{"path":146,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":147,"sourceHtmlUrl":148},"scripts\u002Fdeploy_model.py",9618,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fdeploy_model.py",{"path":150,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":151,"sourceHtmlUrl":152},"scripts\u002Fevaluate_model.py",11690,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fevaluate_model.py",{"path":154,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":155,"sourceHtmlUrl":156},"scripts\u002Fgenerate_distillation_data.py",10299,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fgenerate_distillation_data.py",{"path":158,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":159,"sourceHtmlUrl":160},"scripts\u002Fmonitor_training.py",5866,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fmonitor_training.py",{"path":162,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":163,"sourceHtmlUrl":164},"scripts\u002Fscore_dataset.py",7873,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fscore_dataset.py",{"path":166,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":167,"sourceHtmlUrl":168},"scripts\u002Fsubmit_training.py",11070,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fsubmit_training.py",{"path":170,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":171,"sourceHtmlUrl":172},"scripts\u002Fvalidate\u002F__init__.py",305,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fvalidate\u002F__init__.py",{"path":174,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":175,"sourceHtmlUrl":176},"scripts\u002Fvalidate\u002Fdata_stats.py",6331,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fvalidate\u002Fdata_stats.py",{"path":178,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":179,"sourceHtmlUrl":180},"scripts\u002Fvalidate\u002Fvalidate_dpo.py",3807,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fvalidate\u002Fvalidate_dpo.py",{"path":182,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":183,"sourceHtmlUrl":184},"scripts\u002Fvalidate\u002Fvalidate_rft.py",8799,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fvalidate\u002Fvalidate_rft.py",{"path":186,"resourceType":125,"resourceTypeDesc":126,"sizeBytes":187,"sourceHtmlUrl":188},"scripts\u002Fvalidate\u002Fvalidate_sft.py",4573,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fscripts\u002Fvalidate\u002Fvalidate_sft.py",{"path":190,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":117,"sourceHtmlUrl":191},"workflows\u002Fdataset-creation.md","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fworkflows\u002Fdataset-creation.md",{"path":193,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":194,"sourceHtmlUrl":195},"workflows\u002Fdiagnose-poor-results.md",2401,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fworkflows\u002Fdiagnose-poor-results.md",{"path":197,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":198,"sourceHtmlUrl":199},"workflows\u002Ffull-pipeline.md",3167,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fworkflows\u002Ffull-pipeline.md",{"path":201,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":202,"sourceHtmlUrl":203},"workflows\u002Fiterative-training.md",3178,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fworkflows\u002Fiterative-training.md",{"path":205,"resourceType":75,"resourceTypeDesc":76,"sizeBytes":206,"sourceHtmlUrl":207},"workflows\u002Fquickstart.md",4600,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FGitHub-Copilot-for-Azure\u002Fblob\u002Fmain\u002Fplugins\u002Fazure-skills\u002Fskills\u002Fmicrosoft-foundry\u002Ffinetuning\u002Fworkflows\u002Fquickstart.md",[209,222,245,261,286,308],{"skillNo":210,"name":211,"nameZh":6,"description":212,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":213,"categories":214,"keywords":218,"publishedAt":45},"01M6D2HQYSQD9N7MTNDT4A7CBM","demo-skill","Project-scope demo skill for tests.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fworkspace\u002F.agents\u002Fskills\u002Fdemo-skill\u002FSKILL.md",[215],{"categoryNo":216,"categoryName":217,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[219,220,44,221,39],"demo skill","demo","project",{"skillNo":223,"name":224,"nameZh":6,"description":225,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":226,"categories":227,"keywords":234,"publishedAt":45},"01M6D2HM0GCPM0TM2HPG9HHR2R","writing","Writing words people read: the specific thing instead of claims, words to drop, sentences, interface copy (buttons, labels, errors, empty states, confirmations), page copy, notifications and emails, alt text and link text, numbers and dates, words that translate well, documentation and voice. Read before writing a page's copy, an interface's text or a document.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fwriting\u002FSKILL.md",[228,231],{"categoryNo":229,"categoryName":230,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":232,"categoryName":233,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000036","内容创作者",[224,235,236,237,238,239,39,240,241,242,243,244],"text","emails","empty","drop","document","page","voice","notifications","documentation","translate",{"skillNo":246,"name":247,"nameZh":6,"description":248,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":249,"categories":250,"keywords":254,"publishedAt":45},"01M6D2HG07YS49NM196Q0R3AZ8","ui","Designing or restyling an interface: direction, layout, type, colour, icons, components, states, responsive behaviour. Read before building a page, a screen or a set of components.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui\u002FSKILL.md",[251],{"categoryNo":252,"categoryName":253,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000032","设计师",[247,255,256,257,258,240,259,39,260],"icons","components","SET","responsive","layout","before building",{"skillNo":262,"name":263,"nameZh":6,"description":264,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":265,"categories":266,"keywords":271,"publishedAt":45},"01M6D2HB59HZHWG1RYZDV4SJ91","ui-themes","Light and dark themes, and colour that holds in both: tokens with a value per theme, and what each kind of content needs to stay visible (icons, SVG drawings and line art, logos, photos, screenshots, charts, code, maps, borders, shadows, focus, form controls), switching without a flash, and checking both. Read before building a second theme or a theme toggle, and before any drawing, logo or chart on a page that has two themes.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui-themes\u002FSKILL.md",[267,270],{"categoryNo":268,"categoryName":269,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[247,272,273,274,275,276,277,278,279,39,260,280,240,281,282,283,255,284,285],"ui themes","switching","photos","tokens","drawing","focus","maps","Art","flash","code","Charts","content","screenshots","logo",{"skillNo":287,"name":288,"nameZh":6,"description":289,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":290,"categories":291,"keywords":295,"publishedAt":45},"01M6D2H5R5KRAE524C8QX0T7Q0","ui-structure","Structure before styling: the blueprint written before any interface code (the screen's job, the regions its kind of page needs, their order and why, the layout skeleton in numbers, the components and their states), the anatomy and order of a homepage, a dashboard, a list, a detail page, settings, a form, sign-in, pricing, docs, an article and a checkout, and how to turn the blueprint into code (tokens, layout primitives, components, sections, page). Read first, before building or rebuilding any page or screen.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui-structure\u002FSKILL.md",[292,293,294],{"categoryNo":229,"categoryName":230,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},{"categoryNo":232,"categoryName":233,"taxonomyType":26,"taxonomyTypeDesc":27},[296,247,297,256,298,299,281,300,301,302,259,275,303,304,305,39,260,240,306,307],"structure","ui structure","pricing","dashboard","Blueprint","article","anatomy","styling","primitives","list","why","how",{"skillNo":309,"name":310,"nameZh":6,"description":311,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":312,"categories":313,"keywords":321,"publishedAt":45},"01M6D2H0ZZBJG77MBGPZHA6MWD","ui-stack-vue","Building interfaces in Vue 3 and Nuxt: project layout, script setup with typed defineProps, defineEmits and defineModel, attribute fallthrough, slots and scoped slots, template refs, ref, reactive and computed, watch versus watchEffect, composables and VueUse, provide and inject, Pinia, keyed v-for, transitions, Nuxt 4 (app directory, pages, layouts, useFetch and useAsyncData keys, $fetch in handlers, server routes, runtime config, useSeoMeta, NuxtImg, middleware), VeeValidate forms, Reka UI and Nuxt UI, testing, vue-tsc, and the traps. Read when the project uses Vue or Nuxt.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui-stack-vue\u002FSKILL.md",[314,317,320],{"categoryNo":315,"categoryName":316,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":318,"categoryName":319,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":252,"categoryName":253,"taxonomyType":26,"taxonomyTypeDesc":27},[247,322,29,323,324,325,326,327,259,221,328,329,39,330,331,332,333,33,334,335],"vue","ui stack vue","forms","pages","watch","runtime","transitions","fetch","pinia","nuxt","template","server","directory","testing"]