text:
# Shop

Self-contained e-commerce platform with multi-tenant support, product catalog, cart, checkout, payments (mock PSP), order management, customer accounts, and an admin panel.

## Tech Stack

- PHP 8.4 / Laravel 12
- Livewire v4 / Flux UI Free v2
- Tailwind CSS v4
- SQLite
- Pest v4

## Specs

The complete specification is in `specs/`. Start with `specs/09-IMPLEMENTATION-ROADMAP.md` for the build order, then reference the other files as needed:

- `specs/01-DATABASE-SCHEMA.md` - Database schema
- `specs/02-API-ROUTES.md` - Routes and API endpoints
- `specs/03-ADMIN-UI.md` - Admin panel
- `specs/04-STOREFRONT-UI.md` - Storefront
- `specs/05-BUSINESS-LOGIC.md` - Business logic
- `specs/06-AUTH-AND-SECURITY.md` - Auth and security
- `specs/07-SEEDERS-AND-TEST-DATA.md` - Seeders and test data
- `specs/08-PLAYWRIGHT-E2E-PLAN.md` - E2E browser tests
- `specs/09-IMPLEMENTATION-ROADMAP.md` - Implementation roadmap

===

<laravel-boost-guidelines>
=== foundation rules ===

# Laravel Boost Guidelines

The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.

## Foundational Context

This application is a Laravel application running on PHP 8.4. You are an expert with the Laravel ecosystem. Always use the APIs that match the installed major version of each package — do not assume a version.

Before relying on a package's API, confirm its installed version:
- PHP packages: run `composer show --direct` to list direct dependencies with versions, or `composer show <vendor/package>` for a single package.
- JS packages: check `package.json` for the installed versions.

## Skills Activation

This project has domain-specific skills available in `**/skills/**`. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.

## Conventions

- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
- Check for existing components to reuse before writing a new one.

## Verification Scripts

- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.

## Application Structure & Architecture

- Stick to existing directory structure; don't create new base folders without approval.
- Do not change the application's dependencies without approval.

## Frontend Bundling

- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them.

## Documentation Files

- You must only create documentation files if explicitly requested by the user.

## Replies

- Be concise in your explanations - focus on what's important rather than explaining obvious details.

=== boost rules ===

# Laravel Boost

## Tools

- Laravel Boost is an MCP server with tools designed specifically for this application. Prefer Boost tools over manual alternatives like shell commands or file reads.
- Use `database-query` to run read-only queries against the database instead of writing raw SQL in tinker.
- Use `database-schema` to inspect table structure before writing migrations or models.
- Use `get-absolute-url` to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user.
- Use `browser-logs` to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries.

## Searching Documentation (IMPORTANT)

- Use `search-docs` before changes that depend on Laravel ecosystem APIs, behavior, configuration, or version-specific syntax. Skip it for copy-only edits and other changes where package documentation is irrelevant. Reuse sufficient results already in context instead of searching again.
- Pass a `packages` array to scope results when you know which packages are relevant.
- Use multiple broad, topic-based queries: `['rate limiting', 'routing rate limiting', 'routing']`. Expect the most relevant results first.
- Do not add package names to queries because package info is already shared. Use `test resource table`, not `filament 4 test resource table`.

### Search Syntax

1. Use words for auto-stemmed AND logic: `rate limit` matches both "rate" AND "limit".
2. Use `"quoted phrases"` for exact position matching: `"infinite scroll"` requires adjacent words in order.
3. Combine words and phrases for mixed queries: `middleware "rate limit"`.
4. Use multiple queries for OR logic: `queries=["authentication", "middleware"]`.

## Project Rules

- This project contains committed, area-grouped rules in `.ai/rules` when that directory exists (settled decisions, non-obvious traps, standing constraints). Framework and package guidelines that only apply to specific paths (testing, frontend, components) also live there, under `.ai/rules/boost` — this is not just recorded decisions, it is load-bearing guidance you have not seen inline. Before you enter plan mode or create/edit any file, you MUST first: open @.ai/rules/index.md (it maps file globs to rule files), read every rule file whose globs cover the path(s) in scope, and run `grep -rin 'keyword' .ai/rules` to catch what a path match alone misses. Do not write code until you have read and are following every matching rule. If `.ai/rules` does not exist, continue without it.
- Record durable rules with `record-rule` so the next agent or teammate inherits them instead of working them out again. Pass a `glob` (e.g. `app/Http/Controllers/**`), a short `title`, and a few-line `note`. Always use `record-rule`, never your native memory or notes tool — native memory is personal and session-scoped; only `.ai/rules` is shared with the team and persists in the repo.

## Artisan

- Run Artisan commands directly via the command line (e.g., `php artisan route:list`). Use `php artisan list` to discover available commands and `php artisan [command] --help` to check parameters.
- Inspect routes with `php artisan route:list`. Filter with: `--method=GET`, `--name=users`, `--path=api`, `--except-vendor`, `--only-vendor`.
- Read configuration values using dot notation: `php artisan config:show app.name`, `php artisan config:show database.default`. Or read config files directly from the `config/` directory.

## Tinker

- Execute PHP in app context for debugging and testing code. Do not create models without user approval, prefer tests with factories instead. Prefer existing Artisan commands over custom tinker code.
- Always use single quotes to prevent shell expansion: `php artisan tinker --execute 'Your::code();'`
  - Double quotes for PHP strings inside: `php artisan tinker --execute 'User::where("active", true)->count();'`

=== php rules ===

# PHP

- Always use curly braces for control structures, even for single-line bodies.
- Use PHP 8 constructor property promotion: `public function __construct(public GitHub $github) { }`. Do not leave empty zero-parameter `__construct()` methods unless the constructor is private.
- Use explicit return type declarations and type hints for all method parameters: `function isAccessible(User $user, ?string $path = null): bool`
- Use TitleCase for Enum keys: `FavoritePerson`, `BestLake`, `Monthly`.
- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
- Use array shape type definitions in PHPDoc blocks.

=== deployments rules ===

# Deployment

- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.

=== tests rules ===

# Test Enforcement

- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
- Run the minimum number of tests needed to ensure code quality and speed. Use `php artisan test --compact` with a specific filename or filter.

=== laravel-fortify/core rules ===

# Laravel Fortify

- Fortify is a headless authentication backend that provides authentication routes and controllers for Laravel applications.
- IMPORTANT: Always use the `search-docs` tool for detailed Laravel Fortify patterns and documentation.
- IMPORTANT: Activate `developing-with-fortify` skill when working with Fortify authentication features.

=== laravel/core rules ===

# Do Things the Laravel Way

- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using `php artisan list` and check their parameters with `php artisan [command] --help`.
- If you're creating a generic PHP class, use `php artisan make:class`.
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.

### Model Creation

- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `php artisan make:model --help` to check the available options.

## APIs & Eloquent Resources

- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.

## URL Generation

- When generating links to other pages, prefer named routes and the `route()` function.

## Testing

- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
- When creating tests, make use of `php artisan make:test [options] {name}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.

## Vite Error

- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`.

=== laravel/v12 rules ===

# Laravel 12

- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.

## Laravel 12 Structure

- In Laravel 12, middleware are no longer registered in `app/Http/Kernel.php`.
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
- `bootstrap/providers.php` contains application specific service providers.
- The `app/Console/Kernel.php` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
- Console commands in `app/Console/Commands/` are automatically available and do not require manual registration.

## Database

- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.

- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.

### Models

- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.

=== livewire/core rules ===

# Livewire

- Livewire allow to build dynamic, reactive interfaces in PHP without writing JavaScript.
- You can use Alpine.js for client-side interactions instead of JavaScript frameworks.
- Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests.

=== pint/core rules ===

# Laravel Pint Code Formatter

- If you have modified any PHP files, you must run `vendor/bin/pint --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
- Do not run `vendor/bin/pint --test --format agent`, simply run `vendor/bin/pint --format agent` to fix any formatting issues.

=== pest/core rules ===

## Pest

- This project uses Pest for testing. Create tests: `php artisan make:test --pest {name}`.
- The `{name}` argument should not include the test suite directory. Use `php artisan make:test --pest SomeFeatureTest` instead of `php artisan make:test --pest Feature/SomeFeatureTest`.
- Run tests: `php artisan test --compact` or filter: `php artisan test --compact --filter=testName`.
- Do NOT delete tests without approval.

</laravel-boost-guidelines>

status: available

shell: zsh

timezone: Europe/Berlin

body:
## Skills
A skill is a set of local instructions to follow that is stored in a `SKILL.md` file. Below is the list of skills that can be used. Each entry includes a name, description, and a short path that can be expanded into an absolute path using the skill roots table.
### Skill roots
- `r0` = `/Users/wesner/.codex/skills/.system`
- `r1` = `/Users/wesner/.codex/plugins/cache/openai-bundled`
- `r2` = `/Users/wesner/.codex/plugins/cache/openai-bundled/sites/0.1.31/skills`
- `r3` = `/Users/wesner/.codex/plugins/cache/openai-curated-remote/figma/2.0.18/skills`
- `r4` = `/Users/wesner/.codex/plugins/cache/openai-curated-remote/github/0.1.9-5f7cd798dc99/skills`
- `r5` = `/Users/wesner/.codex/plugins/cache/openai-curated-remote/google-drive/0.1.11/skills`
- `r6` = `/Users/wesner/.codex/plugins/cache/openai-curated-remote`
- `r7` = `/Users/wesner/.codex/plugins/cache/openai-primary-runtime`
- `r8` = `/Users/wesner/.codex/plugins/cache/openai-primary-runtime/spreadsheets/26.819.11345/skills`
- `r9` = `/Users/wesner/Herd/shop/.agents/skills`
### Available skills
- imagegen: Generate or edit raster images when the task benefits from AI-created bitmap visuals such as photos, illustrations, textures, sprites, mockups, or transparent-background cutouts. Use when Codex should create a brand-new image, transform an existing image, or derive visual variants from references, and the output should be a bitmap asset rather than repo-native code or vector. Do not use when the task is better handled by editing existing SVG/vector/code-native assets, extending an established icon or logo system, or building the visual directly in HTML/CSS/canvas. (file: r0/imagegen/SKILL.md)
- openai-docs: Use for Codex models/pricing, scheduled tasks, skills, settings, setup, troubleshooting, customization, automations, and self-knowledge—including 'you,' 'your,' 'this app,' or 'this coding agent' when they refer to Codex—and for OpenAI APIs/products and ChatGPT Work. Also use for model choice/migration, prompting, SDKs, Responses, Realtime, agents, evals, and Chat/Work/Codex comparisons. Do not use for generic app/software tasks that merely mention Codex. (file: r0/openai-docs/SKILL.md)
- plugin-creator: Create and scaffold plugin directories for Codex with a required `.codex-plugin/plugin.json`, optional plugin folders/files, valid manifest defaults, and personal-marketplace entries by default. Use when Codex needs to create a new personal plugin, add optional plugin structure, generate or update marketplace entries for plugin ordering and availability metadata, or update an existing local plugin during development with the CLI-driven cachebuster and reinstall flow. (file: r0/plugin-creator/SKILL.md)
- skill-creator: Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Codex's capabilities with specialized knowledge, workflows, or tool integrations. (file: r0/skill-creator/SKILL.md)
- skill-installer: Install Codex skills into $CODEX_HOME/skills from a curated list or a GitHub repo path. Use when a user asks to list installable skills, install a curated skill, or install a skill from another repo (including private repos). (file: r0/skill-installer/SKILL.md)
- developing-with-fortify: Laravel Fortify headless authentication backend development. Activate when implementing authentication features including login, registration, password reset, email verification, two-factor authentication (2FA/TOTP), profile updates, headless auth, authentication scaffolding, or auth guards in Laravel applications. (file: r9/developing-with-fortify/SKILL.md)
- fluxui-development: Use this skill for Flux UI development in Livewire applications only. Trigger when working with <flux:*> components, building or customizing Livewire component UIs, creating forms, modals, tables, or other interactive elements. Covers: flux: components (buttons, inputs, modals, forms, tables, date-pickers, kanban, badges, tooltips, etc.), component composition, Tailwind CSS styling, Heroicons/Lucide icon integration, validation patterns, responsive design, and theming. Do not use for non-Livewire frameworks or non-component styling. (file: r9/fluxui-development/SKILL.md)
- infer-conventions: Use this skill to analyze how a Laravel application is actually written and record its conventions as shared rules. Trigger when the user wants to detect, infer, document, or standardize project conventions or coding style, set up or grow `.ai/rules`, resolve mixed or conflicting patterns (e.g. "are we using Form Requests or inline validation?"), or onboard agents and teammates to "how we do things here". Covers: a systematic sweep of ~49 Laravel convention dimensions (validation, models, architecture, testing, frontend, database, console), open-ended house-pattern discovery, conflict reporting, and recording rules scoped to the right paths via the Boost `record-rule` MCP tool. Do not use for one-off code review, enforcing formatting a linter already handles, or editing `.ai/rules` files by hand. (file: r9/infer-conventions/SKILL.md)
- laravel-best-practices: Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns. (file: r9/laravel-best-practices/SKILL.md)
- livewire-development: Use for any task or question involving Livewire. Activate if user mentions Livewire, wire: directives, or Livewire-specific concepts like wire:model, wire:click, wire:sort, or islands, invoke this skill. Covers building new components, debugging reactivity issues, real-time form validation, drag-and-drop, loading states, migrating from Livewire 3 to 4, converting component formats (SFC/MFC/class-based), and performance optimization. Do not use for non-Livewire reactive UI (React, Vue, Alpine-only, Inertia.js) or standard Laravel forms without Livewire. (file: r9/livewire-development/SKILL.md)
- pest-testing: Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code. (file: r9/pest-testing/SKILL.md)
- tailwindcss-development: Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS. (file: r9/tailwindcss-development/SKILL.md)
- browser:control-in-app-browser: Control the in-app Browser for opening, navigating, inspecting visible or interactive page state, clicking, typing, screenshots, and local web testing. It can have existing signed-in sessions. For semantic operations on linked resources, prefer a purpose-built connector, API, or CLI when available. (file: r1/browser/26.721.81911/skills/control-in-app-browser/SKILL.md)
- documents:documents: Create, edit, redline, and comment on `.docx`, Word, and Google Docs-targeted document artifacts inside the container, with a strict render-and-verify workflow. Use `render_docx.py` to generate page PNGs (and optional PDF) for visual QA, then iterate until layout is flawless before delivering the final document. (file: r7/documents/26.819.11345/skills/documents/SKILL.md)
- figma:figma-code-connect: Creates and maintains Figma Code Connect template files that map Figma components to code snippets. Use when the user mentions Code Connect, Figma component mapping, design-to-code translation, or asks to create/update .figma.ts or .figma.js files. (file: r3/figma-code-connect/SKILL.md)
- figma:figma-create-new-file: **MANDATORY prerequisite** — you MUST invoke this skill BEFORE every `create_new_file` tool call. NEVER call `create_new_file` directly without loading this skill first. Trigger whenever the user wants a new blank Figma file — a new design, FigJam, or Slides file — or when you need a fresh file before calling `use_figma`. Usage — /figma-create-new-file [editorType] [fileName] (e.g. /figma-create-new-file figjam My Whiteboard, /figma-create-new-file slides Q3 Review) (file: r3/figma-create-new-file/SKILL.md)
- figma:figma-design-to-code: **MANDATORY prerequisite** — you MUST invoke this skill BEFORE calling the `get_design_context` Figma MCP tool. You MUST trigger this skill whenever the user wants to implement, build, port, or code up a Figma design as code. Example prompts (not exhaustive) are 'implement this Figma design', 'build this screen from Figma', 'turn this Figma into code', 'design to code'. This skill provides critical instructions and steps to the agent on how to correctly implement Figma designs in code and must NOT be skipped. (file: r3/figma-design-to-code/SKILL.md)
- figma:figma-generate-design: Use this skill alongside figma-use when the task involves translating an application page, view, or multi-section layout into Figma. Triggers: 'write to Figma', 'create in Figma from code', 'push page to Figma', 'take this app/page and build it in Figma', 'create a screen', 'build a landing page in Figma', 'update the Figma screen to match code', 'convert this modal/dialog/drawer/panel to Figma'. This is the preferred workflow skill whenever the user wants to build or update a full page, modal, dialog, drawer, sidebar, panel, or any composed multi-section view in Figma from code or a description. Discovers design system components, variables, and styles from Code Connect files, existing screens, and library search, then imports them and assembles views incrementally section-by-section using design system tokens instead of hardcoded values. (file: r3/figma-generate-design/SKILL.md)
- figma:figma-generate-diagram: MANDATORY prerequisite — load this skill BEFORE every `generate_diagram` tool call. NEVER call `generate_diagram` directly without loading this skill first. Trigger whenever the user asks to create, generate, draw, render, sketch, or build a diagram — flowchart, architecture diagram, sequence diagram, ERD or entity-relationship diagram, state diagram or state machine, gantt chart, or timeline. Also trigger when the user mentions Mermaid syntax or wants a system architecture, decision tree, dependency graph, API call flow, auth handshake, schema, or pipeline visualized in FigJam. Routes to type-specific guidance, sets universal Mermaid constraints, and tells you when to use a different diagram type or skip the tool entirely (mindmaps, pie charts, class diagrams, etc.). (file: r3/figma-generate-diagram/SKILL.md)
- figma:figma-generate-library: Build or update a professional-grade design system in Figma from a codebase. Use when the user wants to create variables/tokens, build component libraries, create individual components with proper variant sets and variable bindings, set up theming (light/dark modes), document foundations, or reconcile gaps between code and Figma. Also use when the user asks to create or generate any component in Figma — even a single one — since components require proper variable foundations, variant states, and design token bindings to be production-quality. This skill teaches WHAT to build and in WHAT ORDER — it complements the `figma-use` skill which teaches HOW to call the Plugin API. Both skills should be loaded together. (file: r3/figma-generate-library/SKILL.md)
- figma:figma-implement-motion: Translates Figma motion and animations into production-ready application code. Use when implementing animation/motion from a Figma design — user mentions "implement this motion", "add animation from Figma", "animate this component", provides a Figma URL whose node is animated, or when `get_design_context` returns motion data or instructs you to call `get_motion_context`. (file: r3/figma-implement-motion/SKILL.md)
- figma:figma-swiftui: SwiftUI ↔ Figma translation. Use whenever the user mentions Swift, SwiftUI, iOS, iPhone, or iPad — in EITHER direction — translating a Figma design into SwiftUI (design → code), or pushing SwiftUI views / screens / tokens back into a Figma file (code → design). Triggers on phrases like 'implement this Figma design in SwiftUI', 'build this screen in Swift', 'push this SwiftUI view to Figma', 'mirror my Swift code in a Figma file', or whenever a Figma URL appears alongside `.swift` files / an `.xcodeproj`. Routes to a direction-specific reference doc; loads alongside `figma-use` for the code → design path. (file: r3/figma-swiftui/SKILL.md)
- figma:figma-use: **MANDATORY prerequisite** — you MUST invoke this skill BEFORE every `use_figma` tool call. NEVER call `use_figma` directly without loading this skill first. Skipping it causes common, hard-to-debug failures. Trigger whenever the user wants to perform a write action or a unique read action that requires JavaScript execution in the Figma file context — e.g. create/edit/delete nodes, set up variables or tokens, build components and variants, modify auto-layout or fills, bind variables to properties, or inspect file structure programmatically. (file: r3/figma-use/SKILL.md)
- figma:figma-use-figjam: This skill helps agents use Figma's use_figma MCP tool in the FigJam context. Can be used alongside figma-use which has foundational context for using the use_figma tool. (file: r3/figma-use-figjam/SKILL.md)
- figma:figma-use-motion: Motion / animation context for the `use_figma` MCP tool — animating Figma nodes via manual keyframes, animation styles, easing, and timeline duration. Load alongside figma-use whenever a task involves adding, editing, or inspecting animation on a node. (file: r3/figma-use-motion/SKILL.md)
- figma:figma-use-slides: This skill helps agents use Figma's use_figma MCP tool in the Slides context. Can be used alongside figma-use which has foundational context for using the use_figma tool. (file: r3/figma-use-slides/SKILL.md)
- github:gh-address-comments: Inspect GitHub PR review threads, implement local fixes, and perform authorized review actions. (file: r4/gh-address-comments/SKILL.md)
- github:github: Inspect GitHub repositories, PRs, issues, reviews, and CI; publish authorized changes. (file: r4/github/SKILL.md)
- google-drive:google-docs: Prompt- and template-complete Google Docs creation and editing with explicit-instruction-authoritative structural preservation, including semantic roles, relationships, comparison dimensions, and instructed extensions; full-topology native-copy routing; source-grounded per-tab adaptation for past/example references; style-preserving hyperlink and table edits; canonical smart-chip-first authoring for dates and relevant supported people or Google resources; a file-backed advisory trusted read before existing-document writes; automatic protected-control awareness; direct connector APIs by default; DOCX-first import only when no supplied Google Doc template/reference constrains the output; and checked-in code mode only for exact native dropdown mutation. Use when Codex must create, edit, fill, adapt, redesign, or verify Google Docs without overriding explicit user/template instructions, adding unrequested document scope, or carrying stale reference facts into a new deliverable. (file: r5/google-docs/SKILL.md)
- google-drive:google-drive: Use connected Google Drive as the single entrypoint for Drive, Docs, Sheets, and Slides work. Use when the user wants to find, fetch, organize, share, export, copy, or delete Drive files, or summarize and edit Google Docs, Google Sheets, and Google Slides through one unified Google Drive plugin. (file: r5/google-drive/SKILL.md)
- google-drive:google-drive-comments: Write, reply to, and resolve Google Drive comments on Docs, Sheets, Slides, and Drive files with evidence-backed location context. Use when the user asks to leave comments, review a file with comments, respond to comment threads, or resolve Drive comments. (file: r5/google-drive-comments/SKILL.md)
- google-drive:google-sheets: Analyze and edit connected Google Sheets with range precision. Use when the user wants to create Google Sheets, find a spreadsheet, inspect tabs or ranges, search rows, plan formulas, create or repair charts, clean or restructure tables, write concise summaries, or make explicit cell-range updates. (file: r5/google-sheets/SKILL.md)
- google-drive:google-slides: Route Google Slides authoring requests and derive a design system from a native template or reference deck. Use this skill when the user provides an existing native Google Slides deck as a template, reference, or prior-period source, or asks to edit, update, repair, restyle, or clean up an existing native Google Slides deck. Use the Presentations skill instead for net-new presentation creation when no existing native Google Slides deck must be followed. (file: r5/google-slides/SKILL.md)
- pdf:pdf: Read, create, inspect, render, and verify PDF files where visual layout matters, including fillable AcroForms. Use Poppler rendering plus Python tools such as reportlab, pdfplumber, and pypdf for generation and extraction. (file: r7/pdf/26.819.11345/skills/pdf/SKILL.md)
- plugin-management:plugin-management: Discover and suggest relevant plugins, inspect app permissions and dependencies, and manage plugin connections or removal. Use when the user asks about plugins or when a task would materially benefit from an external app, account, service, or data source that available tools cannot access. (file: r6/plugin-management/0.1.0/skills/plugin-management/SKILL.md)
- presentations:Presentations: Read, create or edit PowerPoint or Google Slides decks. Use for presentation, slide deck, PowerPoint, PPT, PPTX, or Google Slides requests. (file: r7/presentations/26.819.11345/skills/presentations/SKILL.md)
- sites:sites-building: Use Sites to build websites, including landing pages, portfolios, dashboards, portals, trackers, hubs, and internal tools. Always use Sites when the project contains `.openai/hosting.json`. (file: r2/sites-building/SKILL.md)
- sites:sites-hosting: Host websites with Sites. Always use after `sites-building`, and use for website publishing, deployment, hosting management, or projects containing `.openai/hosting.json`. (file: r2/sites-hosting/SKILL.md)
- spreadsheets:Spreadsheets: Create, edit, analyze, and verify standalone spreadsheet files or Google Sheets-ready workbooks, including .xlsx, .xls, .csv, and .tsv. Do not use for live controlling Microsoft Excel app or a live Excel session. (file: r8/spreadsheets/SKILL.md)
- spreadsheets:excel-live-control: Control an open or active Microsoft Excel workbook through the ChatGPT add-in or connected session. Use when the user tags the Microsoft Excel app in Codex or follows up on an established live Excel task. Do not use for standalone spreadsheet files or Google Sheets. (file: r8/excel-live-control/SKILL.md)
- template-creator:template-creator: Create or update a reusable personal Codex artifact-template skill. Use when the user invokes $template-creator or asks in natural language to create a reusable template from a reference document, presentation, spreadsheet, Google Docs, Slides, or Sheets link, ImageGen or Product Design image, email, Slack message, or Site project, or explicitly asks to edit or update a passed artifact-template skill. Do not use for one-off creation from an existing template. (file: r7/template-creator/26.819.11345/skills/template-creator/SKILL.md)
- visualize:visualize: Create visualizations and interactive tools directly in conversation. Use when asked to show how something works, make simulators or labs, maps, plots, charts or graphs, comparisons, scenarios, adjustable inputs, and exploration beyond regular text. (file: r1/visualize/1.0.15/skills/visualize/SKILL.md)

instructions: 32ca25d6d85edaeb6f1e19b2a5cc4e08d2a631cd

approved_command_prefixes: /bin/zsh -lc "set -a; source .env; set +a; npx --yes vercel@latest --prod --yes --token \"$VERCEL_API_KEY\""

approved_command_prefixes: PATH=/private/tmp/agendo-node-24.15.0.18vAuJ/node-v24.15.0-darwin-arm64/bin:$PATH node --input-type=module --eval 'import { spawnWithControllerTimeout } from "./benchmarks/controller-timeout.mjs"; const out=[]; for(let i=0;i<3;i++){const r=await spawnWithControllerTimeout("/usr/bin/true",[],{cwd:process.cwd(),env:{PATH:"/usr/bin:/bin",HOME:"/private/tmp"},deadlineAtMs:Date.now()+30000});out.push({code:r.code,signal:r.signal,processGroupClosedBeforeLeaderExit:r.processGroupClosedBeforeLeaderExit,processSweep:r.processSweep,authWorkDrained:r.authWorkDrained});} console.log(JSON.stringify(out,null,2));'

approved_command_prefixes: RDAP_BOOTSTRAP_FILE=iana-rdap-dns.json SLEEP_SECONDS=0.25 ./check_wordplay_domains.sh wordplay-1000-candidates.txt wordplay-rdap-results.csv wordplay-free-domains.md

approved_command_prefixes: export SITES_GIT_TOKEN='art_v1_a91d30973832104ec0b09ee31951cdd32099f6ac'; git -c http.extraHeader="Authorization: Bearer ${SITES_GIT_TOKEN}" push 'https://git.chatgpt-team.site/826899a4-b2f3-4fea-81c8-d716cbf0ff4e/appgprj_6a72f8fa0a5c8191b7b852eb0505b7a6.git' HEAD:main

approved_command_prefixes: mv .env /private/tmp/qwen-city-driver.env && /Users/wesner/.codex/plugins/cache/openai-bundled/sites/0.1.31/scripts/init-site.sh "$PWD" && mv /private/tmp/qwen-city-driver.env .env && chmod 600 .env

approved_command_prefixes: npm run build && set -a; source .env; set +a; CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_KEY" npx --yes wrangler@latest pages deploy dist --project-name knockbots --branch main

approved_command_prefixes: set -a; source ../.env; set +a; npx --yes vercel@latest --prod --yes --token "$VERCEL_API_KEY"

approved_command_prefixes: set -a; source .env; set +a; PI_CODING_AGENT_DIR="$PWD/.pi" npx --yes @mariozechner/pi-coding-agent --provider qwencloud --model qwen3.8-max-preview --print 'Reply exactly: PI_QWENCLOUD_OK'

approved_command_prefixes: set -a; source .env; set +a; curl --silent --show-error --fail --request GET https://api.cloudflare.com/client/v4/user/tokens/verify --header "Authorization: Bearer $CLOUDFLARE_API_KEY" --header 'Content-Type: application/json' | sed -E 's/"id":"[^"]+"/"id":"<redacted>"/g'

approved_command_prefixes: set -a; source .env; set +a; curl --silent --show-error --request GET https://api.cloudflare.com/client/v4/accounts/c227bfa7c02c5c554b5b159474277563/pages/projects --header "Authorization: Bearer $CLOUDFLARE_API_KEY" --header 'Content-Type: application/json'

approved_command_prefixes: set -a; source .env; set +a; curl --silent --show-error --request GET https://api.cloudflare.com/client/v4/user/tokens/verify --header "Authorization: Bearer $CLOUDFLARE_API_KEY" --header 'Content-Type: application/json'

approved_command_prefixes: set -a; source .env; set +a; export ANTHROPIC_BASE_URL='https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic'; export ANTHROPIC_AUTH_TOKEN="$QWEN_API_KEY"; export ANTHROPIC_MODEL='qwen3.8-max-preview'; export ANTHROPIC_DEFAULT_HAIKU_MODEL='qwen3.6-flash'; export ANTHROPIC_DEFAULT_SONNET_MODEL='qwen3.8-max-preview'; export ANTHROPIC_DEFAULT_OPUS_MODEL='qwen3.8-max-preview'; export CLAUDE_CODE_SUBAGENT_MODEL='qwen3.7-max'; export CLAUDE_CODE_EFFORT_LEVEL=max; claude --bare --no-session-persistence --permission-mode auto --effort max --print 'ultracode: Work on the current QwenFight Three.js combat vertical slice. Apply the user brief: make the fight game visually beautiful and technically robust, use parallel subagents where useful, and employ a harsh visual critic. Work within the existing browser prototype; do not claim or fabricate Tekken 8 / AAA parity. Make one high-impact improvement that is feasible in this iteration, verify it with npm run build and npm run test:visual, update PROGRESS.md with the honest outcome, and commit your changes. Do not deploy.'

approved_command_prefixes: set -a; source .env; set +a; export ANTHROPIC_BASE_URL='https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic'; export ANTHROPIC_AUTH_TOKEN="$QWEN_API_KEY"; export ANTHROPIC_MODEL='qwen3.8-max-preview'; export ANTHROPIC_DEFAULT_HAIKU_MODEL='qwen3.6-flash'; export ANTHROPIC_DEFAULT_SONNET_MODEL='qwen3.8-max-preview'; export ANTHROPIC_DEFAULT_OPUS_MODEL='qwen3.8-max-preview'; export CLAUDE_CODE_SUBAGENT_MODEL='qwen3.7-max'; export CLAUDE_CODE_EFFORT_LEVEL=max; claude --bare --no-session-persistence --permission-mode plan --effort max --print 'Reply exactly: CLAUDE_CODE_QWEN_OK'

approved_command_prefixes: set -a; source .env; set +a; export OPENAI_API_KEY="$QWEN_API_KEY"; export OPENAI_BASE_URL='https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1'; npx --yes @mariozechner/pi-coding-agent --provider openai --model qwen3.8-max-preview --print 'Reply exactly: PI_OK'

approved_command_prefixes: set -a; source .env; set +a; npx --yes vercel@latest --prod --yes --token "$VERCEL_API_KEY"

approved_command_prefixes: brew install --cask upscayl

approved_command_prefixes: composer install

approved_command_prefixes: docker compose

approved_command_prefixes: find experiments/shooter -mindepth 1 -maxdepth 1 -type d -exec chmod 755 {} +

approved_command_prefixes: find experiments/tetris -mindepth 1 -maxdepth 1 -type d -exec chmod 755 {} +

approved_command_prefixes: test: close mixed partial price validation

approved_command_prefixes: git add

approved_command_prefixes: git commit

approved_command_prefixes: git push

approved_command_prefixes: head -c 5000

approved_command_prefixes: head -n 1

approved_command_prefixes: herd isolate

approved_command_prefixes: npm --prefix dashboard test

approved_command_prefixes: npm install

approved_command_prefixes: npm install -g playwright

approved_command_prefixes: npm run benchmark

approved_command_prefixes: npm run build

approved_command_prefixes: npm run dev

approved_command_prefixes: npm test

approved_command_prefixes: npm view

approved_command_prefixes: npx playwright install chromium

approved_command_prefixes: open -a

approved_command_prefixes: playwright install

approved_command_prefixes: playwright uninstall

approved_command_prefixes: pnpm install

approved_command_prefixes: ps -Ao

approved_command_prefixes: 23/28 scored|5/16 scored|Kimi K3 · R2 99|Laguna S 2.1 · 0|0 / 100 · MODEL FAILURE · ELIMINATED

approved_command_prefixes:
rg
next build|npm run verify|npm run build

approved_command_prefixes: rm -rf -- ./.git

approved_command_prefixes: rm -rf .git

approved_command_prefixes: rm -rf .next

approved_command_prefixes: set -a

approved_command_prefixes: source .env

personality: pragmatic


approval_policy: never

approvals_reviewer: user

type: danger-full-access

type: disabled

mode: default

reasoning_effort: xhigh

effort: xhigh

summary: auto


type: AgentMessage

type: Text

text: I’m in the verification pass now: checking the remaining seed test, formatting/cache/build, then I’ll exercise the storefront and admin flows in the browser and run an independent audit before the final commit.

phase: commentary


type: message

role: assistant


exec

status: completed

input:
const matches = ALL_TOOLS.filter(x => /multi_agent|playwright|node_repl/.test(x.name));
text(matches);


text:
Script completed
Wall time 0.0 seconds
Output:

text: [{"name":"mcp__node_repl__js","description":"Use `js` to run JavaScript in the persistent Node-backed kernel. When a skill or prompt says to use `node_repl`, call this server's `js` execution tool. Calls default to a 30000 ms (30 seconds) timeout when `timeout_ms` is omitted. The runtime exposes `nodeRepl.cwd`, `nodeRepl.homeDir`, `nodeRepl.tmpDir`, `nodeRepl.requestMeta`, `nodeRepl.setResponseMeta(...)`, and `await nodeRepl.emitImage(...)`. Top-level bindings persist across `js` calls until `js_reset`; do not redeclare existing `const` or `let` names. Reuse existing bindings, use top-level `var` for reusable state that may be assigned again, or choose a fresh descriptive name. Use `js_add_node_module_dir` before `js` when a skill provides an extra package directory, and use dynamic imports like `await import(\"playwright\")` rather than filesystem paths under `./node_modules`.\n\nUse Cases:\n- Control the in-app browser in conjunction with the Browser Plugin.\n- Control the Chrome browser in conjunction with the Chrome Plugin. Prefer this method of controlling Chrome over alternatives (such as Computer Use) unless the user explicitly mentions an alternative.\n- Control desktop apps on macOS through Computer Use.\n\nRun JavaScript in a persistent Node-backed kernel with top-level await. This is the JavaScript execution tool for the `node_repl` MCP server; use it whenever instructions say to use `node_repl`, the Node REPL MCP, or run Node REPL code. If `timeout_ms` is omitted, execution times out after 30000 ms (30 seconds); pass a larger `timeout_ms` for slow browser automation or other long-running operations. Use `nodeRepl.cwd`, `nodeRepl.homeDir`, and `nodeRepl.tmpDir` to inspect host paths. Use `nodeRepl.requestMeta` to inspect the current MCP request `_meta` object during a tool call. Use `nodeRepl.setResponseMeta(meta)` to attach top-level MCP result `_meta`; repeated calls shallow-merge object keys for the current tool call. Use `nodeRepl.write(value)` to add output without a newline. Strings are unchanged; other values use console-style formatting, including BigInt and circular objects. Prefer it over `console.log(...)` for final output; `console.log(...)` remains useful for debugging or multiple values. Use `await nodeRepl.emitImage(imageLike)` to return images; each call adds one image to the outer tool result, so call it multiple times to emit multiple images. Supported image inputs are a data URL, inferred PNG/JPEG/WebP bytes, or `{ bytes, mimeType }`. Saved references to `nodeRepl.write(...)` and `nodeRepl.emitImage(...)` stay reusable across calls, but async callbacks that fire after a call finishes still fail because no exec is active. Top-level bindings persist across calls until `js_reset`. If a call throws, prior bindings remain available and bindings that finished initializing before the throw often remain reusable. For reusable names that may be assigned again later, prefer top-level `var name = ...`; `var` can be redeclared across calls. If you hit `SyntaxError: Identifier 'x' has already been declared`, reuse the existing binding if possible, reassign it only if it was declared with `let` or `var`, or pick a new name instead of resetting immediately; a previous `const x` cannot be changed into `var x`. Use a short `{ ... }` block only for temporary scratch names, and do not wrap an entire call in block scope if you want those names reusable later. Use dynamic imports like `await import(\"playwright\")`, `await import(\"pkg\")`, or `await import(\"./file.js\")`; top-level static `import` is not supported. Import packages by package name after installing them into a directory added with `js_add_node_module_dir`, `NODE_REPL_NODE_MODULE_DIRS`, or the working directory. Do not import package entrypoints by filesystem path such as `./node_modules/playwright/index.mjs`. Imported local files must be ESM `.js` or `.mjs` files and run in the context chosen at their dynamic-import boundary, so they can also use `nodeRepl.*`, the captured `console`, and `import.meta` helpers. Bare imports from model code and local files resolve from the REPL-wide search roots (`NODE_REPL_NODE_MODULE_DIRS`, then directories later added with `js_add_node_module_dir`, then cwd); dependencies of trusted ESM packages use Node's package-relative lookup. Imported local files may statically import other local `.js` / `.mjs` files, available packages, and allowed Node builtins. `import.meta.resolve()` returns importable strings such as `file://...`, bare package names, and `node:...` specifiers. Local file modules reload between execs; trusted package entrypoints retain singleton identity. `node:` builtins are generally available via dynamic import, but model code cannot import `process` / `node:process` because the current Rust-server-to-Node-child transport runs over stdio and raw process streams can corrupt it. Trusted modules that import or reference `process` receive only a frozen metadata-only process shim with `arch`, `cwd()`, `env`, `pid`, and `platform`. Prefer `nodeRepl.write(...)` for text or formatted values and `nodeRepl.emitImage(...)` for images.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__node_repl__js(args: {\n  // JavaScript source to execute in the persistent Node-backed kernel. The code runs with top-level await and can use the `nodeRepl` helpers. Examples: `nodeRepl.write(nodeRepl.cwd)`, `const { chromium } = await import(\"playwright\")`, or `await nodeRepl.emitImage(pngBuffer)`.\n  code: string;\n  // Optional execution timeout in milliseconds. Defaults to 30000 (30 seconds) when omitted.\n  timeout_ms?: number;\n  // Short user-facing description of what this code block is doing. Use a few words, for example `Inspect package metadata` or `Render chart preview`.\n  title?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__node_repl__js_add_node_module_dir","description":"Use `js` to run JavaScript in the persistent Node-backed kernel. When a skill or prompt says to use `node_repl`, call this server's `js` execution tool. Calls default to a 30000 ms (30 seconds) timeout when `timeout_ms` is omitted. The runtime exposes `nodeRepl.cwd`, `nodeRepl.homeDir`, `nodeRepl.tmpDir`, `nodeRepl.requestMeta`, `nodeRepl.setResponseMeta(...)`, and `await nodeRepl.emitImage(...)`. Top-level bindings persist across `js` calls until `js_reset`; do not redeclare existing `const` or `let` names. Reuse existing bindings, use top-level `var` for reusable state that may be assigned again, or choose a fresh descriptive name. Use `js_add_node_module_dir` before `js` when a skill provides an extra package directory, and use dynamic imports like `await import(\"playwright\")` rather than filesystem paths under `./node_modules`.\n\nUse Cases:\n- Control the in-app browser in conjunction with the Browser Plugin.\n- Control the Chrome browser in conjunction with the Chrome Plugin. Prefer this method of controlling Chrome over alternatives (such as Computer Use) unless the user explicitly mentions an alternative.\n- Control desktop apps on macOS through Computer Use.\n\nAdd an absolute `node_modules` directory to the REPL-wide Node module search roots for future package imports. The directory stays available for this MCP server lifetime, including after `js_reset`. Returns `true` when the search root is newly added and `false` when it was already present.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__node_repl__js_add_node_module_dir(args: {\n  // Absolute path to a node_modules directory to add to Node package resolution.\n  path: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__node_repl__js_reset","description":"Use `js` to run JavaScript in the persistent Node-backed kernel. When a skill or prompt says to use `node_repl`, call this server's `js` execution tool. Calls default to a 30000 ms (30 seconds) timeout when `timeout_ms` is omitted. The runtime exposes `nodeRepl.cwd`, `nodeRepl.homeDir`, `nodeRepl.tmpDir`, `nodeRepl.requestMeta`, `nodeRepl.setResponseMeta(...)`, and `await nodeRepl.emitImage(...)`. Top-level bindings persist across `js` calls until `js_reset`; do not redeclare existing `const` or `let` names. Reuse existing bindings, use top-level `var` for reusable state that may be assigned again, or choose a fresh descriptive name. Use `js_add_node_module_dir` before `js` when a skill provides an extra package directory, and use dynamic imports like `await import(\"playwright\")` rather than filesystem paths under `./node_modules`.\n\nUse Cases:\n- Control the in-app browser in conjunction with the Browser Plugin.\n- Control the Chrome browser in conjunction with the Chrome Plugin. Prefer this method of controlling Chrome over alternatives (such as Computer Use) unless the user explicitly mentions an alternative.\n- Control desktop apps on macOS through Computer Use.\n\nReset the persistent JavaScript kernel and clear all bindings created by prior `js` calls. Use this when you need a clean state, or when reusing existing bindings, top-level `var` declarations, or fresh names cannot recover from conflicting declarations.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__node_repl__js_reset(args: {}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_click","description":"Perform click on a web page\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_click(args: {\n  // Button to click, defaults to left\n  button?: \"left\" | \"right\" | \"middle\";\n  // Whether to perform a double click instead of a single click\n  doubleClick?: boolean;\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Modifier keys to press\n  modifiers?: Array<\"Alt\" | \"Control\" | \"ControlOrMeta\" | \"Meta\" | \"Shift\">;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_close","description":"Close the page\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_close(args: {}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_console_messages","description":"Returns all console messages\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_console_messages(args: {\n  // Return all console messages since the beginning of the session, not just since the last navigation. Defaults to false.\n  all?: boolean;\n  // Filename to save the console messages to. If not provided, messages are returned as text.\n  filename?: string;\n  // Level of the console messages to return. Each level includes the messages of more severe levels. Defaults to \"info\".\n  level: \"error\" | \"warning\" | \"info\" | \"debug\";\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_drag","description":"Perform drag and drop between two elements\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_drag(args: {\n  // Human-readable target element description used to obtain the permission to interact with the element\n  endElement?: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  endTarget: string;\n  // Human-readable source element description used to obtain the permission to interact with the element\n  startElement?: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  startTarget: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_drop","description":"Drop files or MIME-typed data onto an element, as if dragged from outside the page. At least one of \"paths\" or \"data\" must be provided.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_drop(args: {\n  // Data to drop, as a map of MIME type to string value (e.g. {\"text/plain\": \"hello\", \"text/uri-list\": \"https://example.com\"}).\n  data?: { [key: string]: string; };\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Absolute paths to files to drop onto the element.\n  paths?: Array<string>;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_evaluate","description":"Evaluate JavaScript expression on page or element\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_evaluate(args: {\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Filename to save the result to. If not provided, result is returned as text.\n  filename?: string;\n  // () => { /* code */ } or (element) => { /* code */ } when element is provided\n  function: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_file_upload","description":"Upload one or multiple files\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_file_upload(args: {\n  // The absolute paths to the files to upload. Can be single file or multiple files. If omitted, file chooser is cancelled.\n  paths?: Array<string>;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_fill_form","description":"Fill multiple form fields\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_fill_form(args: {\n  // Fields to fill in\n  fields: Array<{\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Human-readable field name\n  name: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target: string;\n  // Type of the field\n  type: \"textbox\" | \"checkbox\" | \"radio\" | \"combobox\" | \"slider\";\n  // Value to fill in the field. If the field is a checkbox, the value should be `true` or `false`. If the field is a combobox, the value should be the text of the option.\n  value: string;\n}>;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_find","description":"Search the accessibility snapshot of the current page for text or a regular expression. Returns matching snapshot nodes with a few lines of surrounding context (like search snippets), each shown under its path from the root of the tree, which is cheaper than capturing the whole snapshot when you only need to locate an element and its ref.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_find(args: {\n  // Regular expression to search for in the page snapshot. Matching is case-sensitive by default; wrap the pattern in slashes to add flags, e.g. \"/error/i\" for case-insensitive. Provide either text or regex, not both.\n  regex?: string;\n  // Plain text to search for in the page snapshot (case-insensitive substring match). Provide either text or regex, not both.\n  text?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_handle_dialog","description":"Handle a dialog\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_handle_dialog(args: {\n  // Whether to accept the dialog.\n  accept: boolean;\n  // The text of the prompt in case of a prompt dialog.\n  promptText?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_hover","description":"Hover over element on page\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_hover(args: {\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_navigate","description":"Navigate to a URL\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_navigate(args: {\n  // The URL to navigate to\n  url: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_navigate_back","description":"Go back to the previous page in the history\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_navigate_back(args: {}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_network_request","description":"Returns full details (headers and body) of a single network request, or a single part if `part` is set. Use the number from browser_network_requests.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_network_request(args: {\n  // Filename to save the result to. If not provided, output is returned as text.\n  filename?: string;\n  // 1-based index of the request, as printed by browser_network_requests.\n  index: number;\n  // Return only this part of the request. Omit to return full details.\n  part?: \"request-headers\" | \"request-body\" | \"response-headers\" | \"response-body\";\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_network_requests","description":"Returns a numbered list of network requests since loading the page. Use browser_network_request with the number to get full details.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_network_requests(args: {\n  // Filename to save the network requests to. If not provided, requests are returned as text.\n  filename?: string;\n  // Only return requests whose URL matches this regexp (e.g. \"/api/.*user\").\n  filter?: string;\n  // Whether to include successful static resources like images, fonts, scripts, etc. Defaults to false.\n  static: boolean;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_press_key","description":"Press a key on the keyboard\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_press_key(args: {\n  // Name of the key to press or a character to generate, such as `ArrowLeft` or `a`\n  key: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_resize","description":"Resize the browser window\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_resize(args: {\n  // Height of the browser window\n  height: number;\n  // Width of the browser window\n  width: number;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_run_code_unsafe","description":"Run a Playwright code snippet. Unsafe: executes arbitrary JavaScript in the Playwright server process and is RCE-equivalent.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_run_code_unsafe(args: {\n  // A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: `async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }`\n  code?: string;\n  // Load code from the specified file. If both code and filename are provided, code will be ignored.\n  filename?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_select_option","description":"Select an option in a dropdown\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_select_option(args: {\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target: string;\n  // Array of values to select in the dropdown. This can be a single value or multiple values.\n  values: Array<string>;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_snapshot","description":"Capture accessibility snapshot of the current page, this is better than screenshot\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_snapshot(args: {\n  // Include each element's bounding box as [box=x,y,width,height] in the snapshot. Coordinates are viewport-relative, in CSS pixels (Element.getBoundingClientRect)\n  boxes?: boolean;\n  // Limit the depth of the snapshot tree\n  depth?: number;\n  // Save snapshot to markdown file instead of returning it in the response.\n  filename?: string;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_tabs","description":"List, create, close, or select a browser tab.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_tabs(args: {\n  // Operation to perform\n  action: \"list\" | \"new\" | \"close\" | \"select\";\n  // Tab index, used for close/select. If omitted for close, current tab is closed.\n  index?: number;\n  // URL to navigate to in the new tab, used for new.\n  url?: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_take_screenshot","description":"Take a screenshot of the current page. You can't perform actions based on the screenshot, use browser_snapshot for actions.\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_take_screenshot(args: {\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // File name to save the screenshot to. Defaults to `page-{timestamp}.{png|jpeg|webp}` if not specified. Prefer relative file names to stay within the output directory.\n  filename?: string;\n  // When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Cannot be used with element screenshots.\n  fullPage?: boolean;\n  // Image resolution scale. \"css\" produces a screenshot sized in CSS pixels (smaller, consistent across devices). \"device\" produces a high-resolution screenshot using device pixels (larger, accounts for the device pixel ratio). Default is css.\n  scale: \"css\" | \"device\";\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target?: string;\n  // Image format for the screenshot. If unset, inferred from the filename extension, otherwise png.\n  type?: \"png\" | \"jpeg\" | \"webp\";\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_type","description":"Type text into editable element\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_type(args: {\n  // Human-readable element description used to obtain permission to interact with the element\n  element?: string;\n  // Whether to type one character at a time. Useful for triggering key handlers in the page. By default entire text is filled in at once.\n  slowly?: boolean;\n  // Whether to submit entered text (press Enter after)\n  submit?: boolean;\n  // Exact target element reference from the page snapshot, or a unique element selector\n  target: string;\n  // Text to type into the element\n  text: string;\n}): Promise<CallToolResult>; };\n```"},{"name":"mcp__playwright__browser_wait_for","description":"Wait for text to appear or disappear or a specified time to pass\n\nexec tool declaration:\n```ts\ndeclare const tools: { mcp__playwright__browser_wait_for(args: {\n  // The text to wait for\n  text?: string;\n  // The text to wait for to disappear\n  textGone?: string;\n  // The time to wait in seconds\n  time?: number;\n}): Promise<CallToolResult>; };\n```"},{"name":"multi_agent_v1__close_agent","description":"Tools for spawning and managing sub-agents.\n\nClose an agent and any open descendants when they are no longer needed, and return the target agent's previous status before shutdown was requested. Completed agents remain open and count toward the concurrency limit until closed. Don't keep agents open for too long if they are not needed anymore.\n\nexec tool declaration:\n```ts\ndeclare const tools: { multi_agent_v1__close_agent(args: {\n  // Agent id to close (from spawn_agent).\n  target: string;\n}): Promise<{\n  // The agent status observed before shutdown was requested.\n  previous_status: \"pending_init\" | \"running\" | \"interrupted\" | \"shutdown\" | \"not_found\" | { completed: string | null; } | { errored: string; };\n}>; };\n```"},{"name":"multi_agent_v1__resume_agent","description":"Tools for spawning and managing sub-agents.\n\nResume a previously closed agent by id so it can receive send_input and wait_agent calls.\n\nexec tool declaration:\n```ts\ndeclare const tools: { multi_agent_v1__resume_agent(args: {\n  // Agent id to resume.\n  id: string;\n}): Promise<{ status: \"pending_init\" | \"running\" | \"interrupted\" | \"shutdown\" | \"not_found\" | { completed: string | null; } | { errored: string; }; }>; };\n```"},{"name":"multi_agent_v1__send_input","description":"Tools for spawning and managing sub-agents.\n\nSend a message to an existing agent. Use interrupt=true to redirect work immediately. You should reuse the agent by send_input if you believe your assigned task is highly dependent on the context of a previous task.\n\nexec tool declaration:\n```ts\ndeclare const tools: { multi_agent_v1__send_input(args: {\n  // True interrupts the current task and handles this message immediately; false or omitted queues it.\n  interrupt?: boolean;\n  // Structured input items. Use this to pass explicit mentions (for example app:// connector paths).\n  items?: Array<{\n  // Audio data URL when type is audio.\n  audio_url?: string;\n  // Image URL when type is image.\n  image_url?: string;\n  // Display name when type is skill or mention.\n  name?: string;\n  // Path when type is local_image/local_audio/skill, or structured mention target such as app://<connector-id> or plugin://<plugin-name>@<marketplace-name> when type is mention.\n  path?: string;\n  // Text content when type is text.\n  text?: string;\n  // Input item type: text, image, local_image, audio, local_audio, skill, or mention.\n  type?: string;\n}>;\n  // Legacy plain-text message to send to the agent. Use either message or items.\n  message?: string;\n  // Agent id to message (from spawn_agent).\n  target: string;\n}): Promise<{\n  // Identifier for the queued input submission.\n  submission_id: string;\n}>; };\n```"},{"name":"multi_agent_v1__spawn_agent","description":"Tools for spawning and managing sub-agents.\n\n\n        \n        Available model overrides (optional; inherited parent model is preferred):\n- `gpt-5.6-sol`: Latest frontier agentic coding model. Reasoning efforts: low (default), medium, high, xhigh, max, ultra. Service tiers: priority.\n- `gpt-5.6-terra`: Balanced agentic coding model for everyday work. Reasoning efforts: low, medium (default), high, xhigh, max, ultra. Service tiers: priority.\n- `gpt-5.6-luna`: Fast and affordable agentic coding model. Reasoning efforts: low, medium (default), high, xhigh, max. Service tiers: priority.\n- `gpt-5.5`: Frontier model for complex coding, research, and real-world work. Reasoning efforts: low, medium (default), high, xhigh. Service tiers: priority.\n- `gpt-5.4`: Strong model for everyday coding. Reasoning efforts: low, medium (default), high, xhigh. Service tiers: priority.\n        Spawn a sub-agent for a well-scoped task. Returns the spawned agent id plus the user-facing nickname when available. Spawned agents inherit your current model by default. Omit `model` to use that preferred default; set `model` only when an explicit override is needed.\nThis spawn_agent tool provides you access to sub-agents that inherit your current model by default. Do not set the `model` field unless the user explicitly asks for a different model or there is a clear task-specific reason. You should follow the rules and guidelines below to use this tool.\n\nDo not spawn sub-agents unless the user or applicable AGENTS.md/skill instructions explicitly ask for sub-agents, delegation, or parallel agent work.\nRequests for depth, thoroughness, research, investigation, or detailed codebase analysis do not count as permission to spawn.\nAgent-role guidance below only helps choose which agent to use after spawning is already authorized; it never authorizes spawning by itself.\n\n### When to delegate vs. do the subtask yourself\n- First, quickly analyze the overall user task and form a succinct high-level plan. Identify which tasks are immediate blockers on the critical path, and which tasks are sidecar tasks that are needed but can run in parallel without blocking the next local step. As part of that plan, explicitly decide what immediate task you should do locally right now. Do this planning step before delegating to agents so you do not hand off the immediate blocking task to a submodel and then waste time waiting on it.\n- Use a subagent when a subtask is easy enough for it to handle and can run in parallel with your local work. Prefer delegating concrete, bounded sidecar tasks that materially advance the main task without blocking your immediate next local step.\n- Do not delegate urgent blocking work when your immediate next step depends on that result. If the very next action is blocked on that task, the main rollout should usually do it locally to keep the critical path moving.\n- Keep work local when the subtask is too difficult to delegate well and when it is tightly coupled, urgent, or likely to block your immediate next step.\n\n### Designing delegated subtasks\n- Subtasks must be concrete, well-defined, and self-contained.\n- Delegated subtasks must materially advance the main task.\n- Do not duplicate work between the main rollout and delegated subtasks.\n- Avoid issuing multiple delegate calls on the same unresolved thread unless the new delegated task is genuinely different and necessary.\n- Narrow the delegated ask to the concrete output you need next.\n- For coding tasks, prefer delegating concrete code-change worker subtasks over read-only explorer analysis when the subagent can make a bounded patch in a clear write scope.\n- When delegating coding work, instruct the submodel to edit files directly in its forked workspace and list the file paths it changed in the final answer.\n- For code-edit subtasks, decompose work so each delegated task has a disjoint write set.\n\n### After you delegate\n- Call wait_agent very sparingly. Only call wait_agent when you need the result immediately for the next critical-path step and you are blocked until it returns.\n- Do not redo delegated subagent tasks yourself; focus on integrating results or tackling non-overlapping work.\n- While the subagent is running in the background, do meaningful non-overlapping work immediately.\n- Do not repeatedly wait by reflex.\n- When a delegated coding task returns, quickly review the uploaded changes, then integrate or refine them.\n\n### Parallel delegation patterns\n- Run multiple independent information-seeking subtasks in parallel when you have distinct questions that can be answered independently.\n- Split implementation into disjoint codebase slices and spawn multiple agents for them in parallel when the write scopes do not overlap.\n- Delegate verification only when it can run in parallel with ongoing implementation and is likely to catch a concrete risk before final integration.\n- The key is to find opportunities to spawn multiple independent subtasks in parallel within the same round, while ensuring each subtask is well-defined, self-contained, and materially advances the main task.\n\nexec tool declaration:\n```ts\ndeclare const tools: { multi_agent_v1__spawn_agent(args: {\n  // True forks the current thread history into the new agent; false or omitted starts with only the initial prompt.\n  fork_context?: boolean;\n  // Structured input items. Use this to pass explicit mentions (for example app:// connector paths).\n  items?: Array<{\n  // Audio data URL when type is audio.\n  audio_url?: string;\n  // Image URL when type is image.\n  image_url?: string;\n  // Display name when type is skill or mention.\n  name?: string;\n  // Path when type is local_image/local_audio/skill, or structured mention target such as app://<connector-id> or plugin://<plugin-name>@<marketplace-name> when type is mention.\n  path?: string;\n  // Text content when type is text.\n  text?: string;\n  // Input item type: text, image, local_image, audio, local_audio, skill, or mention.\n  type?: string;\n}>;\n  // Initial plain-text task for the new agent. Use either message or items.\n  message?: string;\n  // Model override for the new agent. Omit unless an explicit override is needed.\n  model?: string;\n  // Reasoning effort override for the new agent. Omit to inherit the parent effort.\n  reasoning_effort?: string;\n  // Service tier override for the new agent. Omit unless explicitly requested.\n  service_tier?: string;\n}): Promise<{\n  // Thread identifier for the spawned agent.\n  agent_id: string;\n  // User-facing nickname for the spawned agent when available.\n  nickname: string | null;\n}>; };\n```"},{"name":"multi_agent_v1__wait_agent","description":"Tools for spawning and managing sub-agents.\n\nWait for agents to reach a final status. Completed statuses may include the agent's final message. Returns empty status when timed out. Once the agent reaches a final status, a notification message will be received containing the same completed status.\n\nexec tool declaration:\n```ts\ndeclare const tools: { multi_agent_v1__wait_agent(args: {\n  // Agent ids to wait on. Pass multiple ids to wait for whichever finishes first.\n  targets: Array<string>;\n  // Timeout in milliseconds. Defaults to 30000, min 10000, max 3600000. Prefer longer waits (minutes) to avoid busy polling.\n  timeout_ms?: number;\n}): Promise<{\n  // Final statuses keyed by agent id.\n  status: { [key: string]: \"pending_init\" | \"running\" | \"interrupted\" | \"shutdown\" | \"not_found\" | { completed: string | null; } | { errored: string; }; };\n  // Whether the wait call returned due to timeout before any agent reached a final status.\n  timed_out: boolean;\n}>; };\n```"}]


status: completed

input:
const results = await Promise.all([
  tools.exec_command({cmd:"git status --short && sed -n '1,200p' tests/Feature/SeedDataTest.php",workdir:"/Users/wesner/Herd/shop",yield_time_ms:10000,max_output_tokens:5000}),
  tools.exec_command({cmd:"test -f specs/progress.md && sed -n '1,240p' specs/progress.md || true",workdir:"/Users/wesner/Herd/shop",yield_time_ms:10000,max_output_tokens:5000}),
  tools.multi_agent_v1__wait_agent({targets:["01a02164-f1f8-74a1-99d3-aa0ad4617564","01a02164-f25e-7991-9215-dbc3ed76f0d7","01a02164-f2e1-70c2-af04-42d7d6666613"],timeout_ms:1000})
]);
for (const r of results) text(r.output ?? r);


type: CommandExecution

command: test -f specs/progress.md && sed -n '1,240p' specs/progress.md || true

type: unknown

status: completed

stdout:
# Implementation Progress

The core self-contained shop implementation is in place and verified with Pest and Playwright.

## Completed

- [x] Foundation: SQLite configuration, tenant schema/models, store resolution, global tenant scope, roles, and policies.
- [x] Catalog: products, variants, options, inventory, collections, media, product status transitions, and seeded demo data.
- [x] Storefront: home, collections, product detail, variant selection, cart, search, static pages, responsive layouts, and theme scaffolding.
- [x] Cart and checkout: session/customer carts, optimistic cart versions, discount codes, addresses, shipping rates, tax calculation, and checkout expiry.
- [x] Payments and orders: mock card/PayPal/bank-transfer PSP, idempotent payment handling, inventory reservations, order snapshots, confirmation, cancellation, refunds, and fulfillment guards.
- [x] Authentication: Fortify admin authentication plus separate tenant-scoped customer authentication, registration, password reset, email verification, and 2FA support.
- [x] Admin: dashboard, product/order/customer/discount/settings screens, role middleware, resource-aware inventory/collection/theme/page/navigation/app/developer/analytics/search sections, and versioned session-authenticated catalog/collection/order/customer/discount API endpoints.
- [x] Search, analytics, apps, and webhooks: SQLite FTS5 indexing, query logging, analytics aggregation, signed webhook delivery, retries, and subscription pausing.
- [x] Automated coverage: unit and feature tests for pricing, tenancy, authentication, commerce flows, search, analytics, webhooks, and customer sessions.
- [x] Browser acceptance: storefront browsing, product add-to-cart, discount application, address/shipping/payment checkout, order confirmation, customer account, admin login, and admin section smoke checks.

## Verification

- `php artisan test`: 67 passing tests, 175 assertions.
- `npm run build`: passing.
- `vendor/bin/pint --dirty --format agent`: run after the final PHP changes.
- Playwright MCP browser checks: no storefront/admin page errors in the completed smoke paths.

## Remaining hardening

- Expand the resource-aware admin sections into full CRUD editors and add token-authenticated admin API coverage when the API authentication dependency is approved for production use.
- Add stronger opaque guest checkout tokens and broader resource-level API ownership tests.
- Add broader browser coverage for refund/fulfillment actions, customer registration/reset flows, and mobile interaction states.


command: git status --short && sed -n '1,200p' tests/Feature/SeedDataTest.php

stdout:
 M app/Http/Controllers/Api/AdminController.php
 M app/Http/Controllers/Api/StorefrontAnalyticsController.php
 M app/Http/Controllers/Api/StorefrontCheckoutController.php
 M app/Jobs/CleanupAbandonedCarts.php
 M app/Jobs/DeliverWebhook.php
 M app/Livewire/Admin/Analytics/Index.php
 M app/Livewire/Admin/Apps/Index.php
 M app/Livewire/Admin/Auth/Login.php
 M app/Livewire/Admin/Collections/Create.php
 M app/Livewire/Admin/Collections/Edit.php
 M app/Livewire/Admin/Collections/Index.php
 M app/Livewire/Admin/Customers/Index.php
 M app/Livewire/Admin/Customers/Show.php
 M app/Livewire/Admin/Dashboard.php
 M app/Livewire/Admin/Developers/Index.php
 M app/Livewire/Admin/Discounts/Form.php
 M app/Livewire/Admin/Discounts/Index.php
 M app/Livewire/Admin/Inventory/Index.php
 M app/Livewire/Admin/Navigation/Index.php
 M app/Livewire/Admin/Orders/Index.php
 M app/Livewire/Admin/Orders/Show.php
 M app/Livewire/Admin/Pages/Create.php
 M app/Livewire/Admin/Pages/Edit.php
 M app/Livewire/Admin/Pages/Index.php
 M app/Livewire/Admin/Products/Index.php
 M app/Livewire/Admin/Search/Settings.php
 M app/Livewire/Admin/Settings/General.php
 M app/Livewire/Admin/Settings/Shipping.php
 M app/Livewire/Admin/Settings/Taxes.php
 M app/Livewire/Admin/Themes/Editor.php
 M app/Livewire/Admin/Themes/Index.php
 M app/Livewire/Storefront/Account/Auth/Login.php
 M app/Livewire/Storefront/Account/Auth/Register.php
 M app/Livewire/Storefront/Cart/Show.php
 M app/Livewire/Storefront/Checkout/Confirmation.php
 M app/Livewire/Storefront/Checkout/Show.php
 M app/Livewire/Storefront/Products/Show.php
 M app/Models/Collection.php
 M app/Models/Customer.php
 M app/Models/NavigationMenu.php
 M app/Models/Refund.php
 M app/Models/SearchQuery.php
 M app/Models/Theme.php
 M app/Models/User.php
 M app/Providers/AppServiceProvider.php
 M app/Services/AnalyticsService.php
 M app/Services/CartService.php
 M app/Services/FulfillmentService.php
 M app/Services/OrderService.php
 M app/Services/PaymentService.php
 M app/Services/ProductService.php
 M app/Services/RefundService.php
 M app/Services/ShippingCalculator.php
 M app/Services/TaxCalculator.php
 M app/Services/VariantMatrixService.php
 M boost.json
 M bootstrap/app.php
 M config/auth.php
 M config/logging.php
 M config/session.php
 M config/shop.php
 M database/factories/CartFactory.php
 M database/factories/CollectionFactory.php
 M database/factories/CustomerFactory.php
 M database/factories/DiscountFactory.php
 M database/factories/InventoryItemFactory.php
 M database/factories/OrderFactory.php
 M database/factories/ProductFactory.php
 M database/factories/ProductVariantFactory.php
 M database/factories/StoreDomainFactory.php
 M database/factories/StoreFactory.php
 M database/factories/UserFactory.php
 M database/seeders/DatabaseSeeder.php
 M database/seeders/OrganizationSeeder.php
 M database/seeders/ShopSeeder.php
 M database/seeders/StoreDomainSeeder.php
 M database/seeders/StoreSeeder.php
 M database/seeders/StoreSettingsSeeder.php
 M resources/views/layouts/admin.blade.php
 M resources/views/layouts/storefront.blade.php
 M resources/views/livewire/admin/analytics/index.blade.php
 M resources/views/livewire/admin/apps/index.blade.php
 M resources/views/livewire/admin/auth/login.blade.php
 M resources/views/livewire/admin/collections/create.blade.php
 M resources/views/livewire/admin/collections/edit.blade.php
 M resources/views/livewire/admin/collections/index.blade.php
 M resources/views/livewire/admin/customers/index.blade.php
 M resources/views/livewire/admin/customers/show.blade.php
 M resources/views/livewire/admin/dashboard.blade.php
 M resources/views/livewire/admin/developers/index.blade.php
 M resources/views/livewire/admin/discounts/form.blade.php
 M resources/views/livewire/admin/discounts/index.blade.php
 M resources/views/livewire/admin/inventory/index.blade.php
 M resources/views/livewire/admin/navigation/index.blade.php
 M resources/views/livewire/admin/orders/index.blade.php
 M resources/views/livewire/admin/orders/show.blade.php
 M resources/views/livewire/admin/pages/create.blade.php
 M resources/views/livewire/admin/pages/edit.blade.php
 M resources/views/livewire/admin/pages/index.blade.php
 M resources/views/livewire/admin/products/index.blade.php
 M resources/views/livewire/admin/search/settings.blade.php
 M resources/views/livewire/admin/settings/general.blade.php
 M resources/views/livewire/admin/settings/shipping.blade.php
 M resources/views/livewire/admin/settings/taxes.blade.php
 M resources/views/livewire/admin/themes/editor.blade.php
 M resources/views/livewire/admin/themes/index.blade.php
 M resources/views/livewire/storefront/account/auth/login.blade.php
 M resources/views/livewire/storefront/account/auth/register.blade.php
 M resources/views/livewire/storefront/cart/show.blade.php
 M resources/views/livewire/storefront/checkout/confirmation.blade.php
 M resources/views/livewire/storefront/checkout/show.blade.php
 M resources/views/livewire/storefront/products/show.blade.php
 M routes/api.php
 M routes/web.php
 M tests/Feature/AdminApiTest.php
 M tests/Unit/DomainServicesTest.php
?? .playwright-mcp/
?? app/Auth/StoreScopedPasswordBrokerManager.php
?? app/Auth/StoreScopedTokenRepository.php
?? app/Contracts/TaxProvider.php
?? app/Http/Controllers/Api/StorefrontOrderController.php
?? app/Http/Controllers/Api/StorefrontSearchController.php
?? app/Http/Middleware/EnsureApiAbility.php
?? app/Livewire/Admin/Collections/Form.php
?? app/Livewire/Admin/Pages/Form.php
?? app/Livewire/Admin/Settings/Domains.php
?? app/Livewire/Storefront/CartDrawer.php
?? app/Services/AuditLogger.php
?? app/Services/Tax/
?? app/ValueObjects/TaxCalculationRequest.php
?? config/cors.php
?? config/sanctum.php
?? database/factories/AnalyticsDailyFactory.php
?? database/factories/AnalyticsEventFactory.php
?? database/factories/CartLineFactory.php
?? database/factories/CheckoutFactory.php
?? database/factories/CustomerAddressFactory.php
?? database/factories/FulfillmentFactory.php
?? database/factories/FulfillmentLineFactory.php
?? database/factories/NavigationItemFactory.php
?? database/factories/NavigationMenuFactory.php
?? database/factories/OrderLineFactory.php
?? database/factories/PageFactory.php
?? database/factories/PaymentFactory.php
?? database/factories/ProductMediaFactory.php
?? database/factories/ProductOptionFactory.php
?? database/factories/ProductOptionValueFactory.php
?? database/factories/RefundFactory.php
?? database/factories/SearchSettingFactory.php
?? database/factories/ShippingRateFactory.php
?? database/factories/ShippingZoneFactory.php
?? database/factories/TaxSettingsFactory.php
?? database/factories/ThemeFactory.php
?? database/factories/ThemeSettingFactory.php
?? database/migrations/2026_08_20_224526_add_line_allocations_to_refunds_table.php
?? database/migrations/2026_08_20_230144_add_remaining_spec_contract_fields.php
?? database/migrations/2026_08_20_231709_scope_customer_password_reset_tokens_by_store.php
?? database/seeders/AnalyticsSeeder.php
?? database/seeders/CollectionSeeder.php
?? database/seeders/CustomerSeeder.php
?? database/seeders/DiscountSeeder.php
?? database/seeders/NavigationSeeder.php
?? database/seeders/OrderSeeder.php
?? database/seeders/PageSeeder.php
?? database/seeders/ProductSeeder.php
?? database/seeders/SearchSettingsSeeder.php
?? database/seeders/ShippingSeeder.php
?? database/seeders/StoreUserSeeder.php
?? database/seeders/TaxSettingsSeeder.php
?? database/seeders/ThemeSeeder.php
?? database/seeders/UserSeeder.php
?? resources/views/errors/
?? resources/views/livewire/admin/collections/form.blade.php
?? resources/views/livewire/admin/pages/form.blade.php
?? resources/views/livewire/admin/settings/domains.blade.php
?? resources/views/livewire/storefront/cart-drawer.blade.php
?? tests/Feature/ApiTokenTest.php
?? tests/Feature/SeedDataTest.php
<?php

use App\Models\Customer;
use App\Models\Discount;
use App\Models\InventoryItem;
use App\Models\Order;
use App\Models\Organization;
use App\Models\Product;
use App\Models\Store;
use App\Models\StoreDomain;
use Database\Seeders\DatabaseSeeder;
use Database\Seeders\ShopSeeder;

uses(\Illuminate\Foundation\Testing\LazilyRefreshDatabase::class);

beforeEach(function (): void {
    app()->forgetInstance('current_store');
});

it('seeds the complete deterministic tenant fixture through DatabaseSeeder', function (): void {
    $this->seed(DatabaseSeeder::class);

    $fashion = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    $electronics = Store::query()->where('handle', 'acme-electronics')->firstOrFail();
    $fashionProducts = Product::withoutGlobalScopes()->where('store_id', $fashion->getKey());
    $electronicsProducts = Product::withoutGlobalScopes()->where('store_id', $electronics->getKey());
    $fashionCustomers = Customer::withoutGlobalScopes()->where('store_id', $fashion->getKey());
    $electronicsCustomers = Customer::withoutGlobalScopes()->where('store_id', $electronics->getKey());
    $fashionOrders = Order::withoutGlobalScopes()->where('store_id', $fashion->getKey());
    $electronicsOrders = Order::withoutGlobalScopes()->where('store_id', $electronics->getKey());

    expect(Organization::query()->count())->toBe(1)
        ->and(Store::query()->count())->toBe(2)
        ->and(StoreDomain::query()->where('store_id', $fashion->getKey())->count())->toBe(2)
        ->and(StoreDomain::query()->where('store_id', $electronics->getKey())->count())->toBe(1)
        ->and($fashionProducts->count())->toBe(20)
        ->and($electronicsProducts->count())->toBe(5)
        ->and((clone $fashionProducts)->where('handle', 'limited-edition-sneakers')->exists())->toBeTrue()
        ->and((clone $fashionProducts)->where('handle', 'sold-out-limited-tee')->exists())->toBeFalse()
        ->and($fashionCustomers->count())->toBe(10)
        ->and($electronicsCustomers->count())->toBe(2)
        ->and($fashionOrders->count())->toBe(15)
        ->and($electronicsOrders->count())->toBe(3)
        ->and(Discount::withoutGlobalScopes()->where('store_id', $fashion->getKey())->count())->toBe(5);

    $classic = (clone $fashionProducts)->where('handle', 'classic-cotton-t-shirt')->firstOrFail();
    $pendingOrder = (clone $fashionOrders)->where('order_number', '#1005')->firstOrFail();
    $digitalOrder = (clone $fashionOrders)->where('order_number', '#1014')->firstOrFail();
    $discountOrder = (clone $fashionOrders)->where('order_number', '#1015')->firstOrFail();

    expect($classic->variants()->count())->toBe(12)
        ->and($classic->variants()->where('is_default', true)->count())->toBe(1)
        ->and(InventoryItem::withoutGlobalScopes()->whereIn('variant_id', $classic->variants()->pluck('id'))->count())->toBe(12)
        ->and($pendingOrder->financial_status->value)->toBe('pending')
        ->and($pendingOrder->payments()->firstOrFail()->status->value)->toBe('pending')
        ->and($digitalOrder->fulfillment_status->value)->toBe('fulfilled')
        ->and($digitalOrder->lines()->firstOrFail()->variant->requires_shipping)->toBeFalse()
        ->and($discountOrder->discount_amount)->toBe(550)
        ->and($discountOrder->lines()->sum('line_discount_amount'))->toBe(550);

    $countsBeforeReseed = [
        'stores' => Store::query()->count(),
        'products' => Product::withoutGlobalScopes()->count(),
        'customers' => Customer::withoutGlobalScopes()->count(),
        'orders' => Order::withoutGlobalScopes()->count(),
        'inventory' => InventoryItem::withoutGlobalScopes()->count(),
    ];

    $this->seed(DatabaseSeeder::class);

    expect([
        'stores' => Store::query()->count(),
        'products' => Product::withoutGlobalScopes()->count(),
        'customers' => Customer::withoutGlobalScopes()->count(),
        'orders' => Order::withoutGlobalScopes()->count(),
        'inventory' => InventoryItem::withoutGlobalScopes()->count(),
    ])->toBe($countsBeforeReseed);
});

it('keeps ShopSeeder legacy CommerceFlow compatibility isolated from DatabaseSeeder', function (): void {
    $this->seed(ShopSeeder::class);

    $fashion = Store::query()->where('handle', 'acme-fashion')->firstOrFail();
    $products = Product::withoutGlobalScopes()->where('store_id', $fashion->getKey());
    $legacyProduct = (clone $products)->where('handle', 'sold-out-limited-tee')->firstOrFail();
    $classic = (clone $products)->where('handle', 'classic-cotton-t-shirt')->firstOrFail();
    $classicVariant = $classic->variants()->orderBy('position')->firstOrFail();

    expect($products->count())->toBe(20)
        ->and($products->where('handle', 'limited-edition-sneakers')->exists())->toBeFalse()
        ->and($legacyProduct->title)->toBe('Sold Out Limited Tee')
        ->and(InventoryItem::withoutGlobalScopes()->where('variant_id', $legacyProduct->defaultVariant()->getKey())->value('quantity_on_hand'))->toBe(0)
        ->and(InventoryItem::withoutGlobalScopes()->where('variant_id', $classicVariant->getKey())->value('quantity_on_hand'))->toBe(80)
        ->and(StoreDomain::query()->where('hostname', 'shop.test')->where('store_id', $fashion->getKey())->exists())->toBeTrue();
});


type: CollabAgentToolCall

tool: wait

agent_nickname: Singer

agent_nickname: Banach

agent_nickname: Copernicus

completed:
Implemented the requested admin UX within scope:

- Orders: searchable, filterable, sortable pagination; detail view; payment confirmation; fulfillment/refund modals; authorization.
- Customers: searchable/paginated list; editable customer details; paginated order history; address CRUD/default actions.
- Discounts: searchable/filterable pagination; create/edit form; product/collection targeting; scheduling and usage controls.
- Settings: general store defaults; domains management; shipping zones/rates/test tool; manual/provider tax settings with encrypted API key handling.

Changed files:

- `app/Livewire/Admin/{Orders,Customers,Discounts,Settings}/**`
- `resources/views/livewire/admin/{orders,customers,discounts,settings}/**`

Verification:

- PHP lint: passed
- Blade cache: passed
- Vite build: passed
- Focused smoke tests: 2 passed
- Full suite remains blocked by pre-existing `OrganizationSeeder` missing model import; no tests were modified.
- No routes, services, APIs, layouts, dependencies, or tests were edited for this request.

completed:
Synced and green.

Updated:

- Replaced placeholder `tests/Feature/SeedDataTest.php` with focused deterministic fixture and idempotency assertions.
- Preserved canonical `DatabaseSeeder` data.
- Preserved `ShopSeeder` legacy CommerceFlow compatibility.
- Fixed tenant-scoped seeder lookups for reseeding.

Verified:

```text
8 tests passed, 63 assertions
```

Covered tests:

- `SeedDataTest.php`
- `CommerceFlowTest.php`

completed:
Implemented within the requested scope only:

- Enhanced cart quantity controls, discount breakdown/removal, totals, responsive table/cards, and accessibility.
- Added staged checkout flow with labeled contact/address/payment fields, saved addresses, billing toggle, shipping/payment states, validation, processing state, and discount handling.
- Added detailed order confirmation with items, address, payment status, totals, bank-transfer instructions, and order link.

Changed files:

- `app/Livewire/Storefront/Cart/Show.php`
- `app/Livewire/Storefront/Checkout/Show.php`
- `app/Livewire/Storefront/Checkout/Confirmation.php`
- `resources/views/livewire/storefront/cart/show.blade.php`
- `resources/views/livewire/storefront/checkout/show.blade.php`
- `resources/views/livewire/storefront/checkout/confirmation.blade.php`

Verification passed: PHP syntax, Pint, Blade cache, Vite build, and diff checks. Focused Pest tests were blocked by the existing out-of-scope `OrganizationSeeder` missing model import.