# Cursor Developer Context

**Audience:** AI coding assistants (Cursor, Copilot, Claude Code, GPT, Codex) and human developers  
**Purpose:** Rules, constraints, and conventions for working safely in this codebase  
**Not a feature catalog** — see `PROJECT_MASTER_DOCUMENTATION.md` and `docs/ARCHITECTURE.md` for system detail

---

# Project Summary

**Jetstream App** is a Laravel 12 + Inertia/Vue 3 platform for **Japanese vehicle export operations**.

The business pipeline:

```
Auction intake → Vehicle listing → Reservation → Checkout → Order → Payments → Invoice / Remittance → Shipment
```

**Roles:** `admin`, `seller`, `customer`, `bidder`, `shipping_agent`, `data_entry` (one role per user).

**Commercial model:** Sellers and admins reserve stock for customers; checkout creates orders; payments and wallet/remittance flows settle balances; admin approves seller checkouts and remittances.

**Stack:** PHP 8.2+, Laravel 12, Jetstream 5, Sanctum, Inertia 2, Vue 3, Vite, Tailwind.

**Central services** (prefer extending these over duplicating logic):

| Service | Domain |
|---------|--------|
| `VehicleReservationService` | Reservations, release, locks |
| `CheckoutApprovalService` | Seller checkout approve/hold/decline, bypass scopes |
| `PaymentService` | Payments, allocations, order status sync |
| `WalletService` | Credits, debits, FIFO remittance allocation |
| `InvoiceService` | PDF generation, email |
| `AuctionVehicleSyncService` | Auction car ↔ vehicle sync |
| `EffectiveScopeGrantResolver` | Runtime permissions |
| `UserAccountStatusService` | Enable/disable users |

---

# Golden Rules

## VERY IMPORTANT — read before any change

### 1. NEVER RUN

```bash
php artisan migrate:fresh
php artisan migrate:fresh --seed
```

### 2. NEVER DROP TABLES

No `DROP TABLE`, `TRUNCATE`, or schema resets in dev or production.

### 3. NEVER DELETE DATA

Do not delete production or shared dev rows unless explicitly requested and approved. No bulk wipes, no "clean slate" fixes.

### 4. USE FORWARD MIGRATIONS ONLY

Schema changes = new migration files + `php artisan migrate`. Never rewrite history on deployed migrations.

### 5. ALWAYS BACK UP BEFORE SCHEMA CHANGES

Backup the database before running migrations that alter tables, especially financial tables.

### 6. NEVER MODIFY CUSTOMER SIDE unless explicitly requested

**Frozen boundary** — do not change as a side effect of internal work:

- Public marketplace (`/`, `/vehicles/{vehicle}`)
- Customer checkout, reservation checkout, pay balance
- Customer dashboard, orders, payments, invoice download
- Payment webhooks (`/webhooks/payments`)
- Wallet/remittance settlement behavior visible to customers

Customer routes use prefix `/customer` and `role:customer` middleware.

### 7. NEVER CHANGE BUSINESS LOGIC without documenting impact

If you change payment totals, reservation status transitions, wallet allocation, remittance verification, or access resolution — state what breaks and what to regression-test.

### 8. NEVER REMOVE EXISTING FEATURES unless explicitly approved

Deprecate behind flags or add; do not delete routes, screens, or workflows the business relies on without explicit approval.

### 9. ALWAYS WRITE TESTS for permission and workflow changes

Feature tests for access control, reservations, payments, remittances, and user management are mandatory when touching those areas.

### 10. PRESERVE BACKWARD COMPATIBILITY

- Do not rename scope keys, status enums, or API response shapes without migration path.
- `User::isActive()` treats null `is_active` as true — do not break that.
- Existing seller/customer/admin show pages must keep working when extending user management.

---

# Protected Workflows

These flows are **production-critical**. Any change requires **targeted regression tests** and explicit awareness of side effects.

## Customer marketplace

| Entry | Risk |
|-------|------|
| `PublicController`, `resources/js/Pages/Public/*` | Wrong visibility rules expose draft stock or hide sellable inventory |

**Test:** published/reserved + `stock_status=available` only; inquiry vs direct purchase mode.

## Customer checkout

| Entry | Risk |
|-------|------|
| `Customer\CheckoutController`, `CustomerPurchaseService` | Breaks CIF pricing, consignee capture, order creation |

**Test:** direct purchase and reservation checkout paths.

## Vehicle reservation flow

| Entry | Risk |
|-------|------|
| `VehicleReservationService` | Lock leaks, duplicate reservations, wrong pricing, missing invoices |

**Statuses:** `active`, `expired`, `converted`, `pending_approval`, `on_hold`

**Test:** create, release, expiry, conversion on first payment.

## Checkout approval flow

| Entry | Risk |
|-------|------|
| `CheckoutApprovalService`, `Admin\CheckoutApprovalController`, `Seller\SellerCheckoutController` | Seller sales stuck in pending; wrongful conversion |

**Test:** approve, hold, decline, `checkout.bypass_approval` override.

## Payment approval flow

| Entry | Risk |
|-------|------|
| `PaymentService`, `Admin\PaymentController::markPaid` | Order balances wrong; reservation never converts |

**Test:** partial payments, mark paid, `payments.bypass_approval` override.

## Wallet allocation flow

| Entry | Risk |
|-------|------|
| `WalletService` | Double spend, wrong FIFO allocation, orphaned allocations |

**Test:** remittance credit → checkout debit → payment allocation linkage.

## Remittance verification flow

| Entry | Risk |
|-------|------|
| `Admin\RemittanceController::verify`, `WalletService::credit` | Customer wallet wrong; double credit |

**Test:** verify idempotency, reject/claim pool, seller delete-own-pending policy.

## Invoice generation flow

| Entry | Risk |
|-------|------|
| `InvoiceService` | Missing PDFs, duplicate invoice numbers, email failures |

**Test:** reservation auto-invoice (idempotent), partial payment manual generate.

## Seller assignment flow

| Entry | Risk |
|-------|------|
| `AuctionCarController::assignSeller`, `AuctionVehicleSyncService` | Wrong seller on vehicle; pool/reservation conflicts |

**Scope:** `auction_intake.assign_seller` is **locked** — admin-only.

---

# Architecture Principles

## Where logic belongs

```mermaid
flowchart TB
    REQ[HTTP Request] --> MW[Middleware]
    MW --> CTRL[Controller]
    CTRL --> FR[Form Request validation]
    CTRL --> SVC[Service — business logic]
    SVC --> MODEL[Model — data + simple accessors]
    CTRL --> POL[Policy — authorization helper]
    SVC --> EVT[Events/Jobs — async side effects]
```

| Layer | Responsibility | Do | Don't |
|-------|----------------|-----|-------|
| **Controllers** | HTTP orchestration, Inertia/JSON responses, authorize + delegate | Call services, return redirects/responses | Complex business rules, direct DB in many places |
| **Services** | Business workflows, transactions, cross-model rules | `PaymentService`, `WalletService`, etc. | HTTP concerns, validation rules |
| **Policies** | Model-level authorization (`view`, `update`, `delete`) | `SellerRemittanceDeletePolicy` | Multi-step workflows |
| **Middleware** | Auth, role, scope, active user | `RoleMiddleware`, `EnsureUserIsActive`, `Enforce*ScopeMiddleware` | Business calculations |
| **Form Requests** | Input validation, `authorize()`, grant-aware field filtering | `StoreManagedUserRequest`, `UpdateDataEntryVehicleRequest` | Order/payment state machines |

## Conventions

- **Role routes** are prefixed: `/admin`, `/seller`, `/customer`, `/bidder`, `/shipping-agent`, `/data-entry`.
- **Inertia pages** live under `resources/js/Pages/{Role}/` or shared `Admin/DataEntry/` for auction intake.
- **Route names** follow `{prefix}.{resource}.{action}` — use Ziggy `route()` in Vue.
- **Admin layout nav** is driven by `resources/js/Layouts/adminNavigation.js` (and role equivalents).
- **Prefer existing services** over new parallel implementations.
- **DB transactions** belong in services for multi-step financial operations.
- **Scope checks in UI:** Inertia shared props from `HandleInertiaRequests`; do not rely on UI hiding alone — enforce server-side.

## Config vs database

| Source | Use |
|--------|-----|
| `config/permissions.php` | Scope catalog, default role grants, route→scope maps |
| `config/scope_manifest.php` | UI metadata, dependencies, risk classes |
| `role_scope_grants` table | Persisted role templates (admin UI) |
| `user_scope_overrides` table | Per-user grant/revoke |

Runtime resolution: **`EffectiveScopeGrantResolver`** — always use this for enforcement, not raw config reads in controllers.

---

# Access Control Principles

## Role grants

- Default templates in `config/permissions.php` → `role_grants`
- Persisted in `role_scope_grants`; editable via Admin → Settings → Access Control
- **Admin** receives all staff scopes automatically (bypasses middleware)
- **Customer** scopes are documentation-only for staff system; customers use `role:customer` middleware, not scope middleware

## User overrides

- Only for: `seller`, `bidder`, `shipping_agent`, `data_entry`
- Effects: `grant`, `revoke`, `inherit` (delete row → fall back to role)
- Stored in `user_scope_overrides` with audit table
- Cannot override **admin** or **customer** users

## Locked scopes

Cannot be assigned via role grants **or** user overrides (`RoleScopePolicy::LOCKED_SCOPES`):

- Financial: `remittances.verify`, `payments.mark_paid`, `orders.mark_paid`, `customers.wallet_credit`
- Lifecycle: `auction_intake.assign_seller`, `vehicles.sections.seller_assignment`
- Meta: `users_access.manage_scopes`, `users_access.assign_roles`
- Also locked: all `settings.*`, all `customer_self_service.*`, all `future_actions`

## Override-only scopes

Assignable **only** per-user (never in role templates):

| Scope | Requires base scope | Enforced in |
|-------|---------------------|-------------|
| `checkout.bypass_approval` | `reservations.checkout` | `SellerCheckoutController::checkout` |
| `payments.bypass_approval` | `reservations.pay_balance` | `SellerCheckoutController::payBalance` |

Granting requires a **reason** in Access Control UI.

## Precedence

```
Admin → always allowed

Editable staff:
  1. Role grants (DB if rows exist, else config defaults)
  2. User override grant → add scope
  3. User override revoke → remove scope
  4. inherit / no row → role grant only

Customer → config customer_self_service list only (no overrides)
```

## Enforcement layers (all must be considered)

1. `role:{role}` middleware — coarse gate
2. `scope.{role}` middleware — when `PERMISSIONS_ENFORCE_*` env true
3. Controller ownership checks (`seller_id`, `customerBelongsToSeller()`)
4. Form request section whitelists (data entry, seller vehicles)
5. `AuctionIntakeScope` enum — field/file visibility per intake role

**Do not** change `EffectiveScopeGrantResolver` or locked scope lists without explicit approval and full access-control test suite.

---

# Database Principles

## Schema changes

- **Avoid** unless required for the task.
- **Forward migrations only** — new files under `database/migrations/`.
- Use `Schema::hasColumn()` / `Schema::hasTable()` guards when migrations must be safe across mixed environments (pattern used in tests and some services).
- SQLite (tests) and MySQL (production) both supported — avoid MySQL-only syntax without fallbacks.

## Financial data

| Rule | Reason |
|------|--------|
| Never modify historical payment amounts in place | Audit trail integrity |
| Never delete `wallet_transactions` casually | Balance reconstruction breaks |
| Never delete verified remittances | Wallet credits depend on them |
| Never overwrite `exchange_rates` rows | `ExchangeRateService` uses `insertOrIgnore` by design |
| Preserve `payment_data` JSON audit fields | Bypass approval and allocation metadata |

## Audit data

Do not delete rows from:

- `role_scope_grant_audits`
- `user_scope_override_audits`
- `payment_histories`

## User account fields

- `is_active`, `disabled_at`, `disabled_by` — managed via `UserAccountStatusService`, not mass-assigned on profile update
- `created_by` — set on user create; do not rewrite on existing users
- `role` — not updateable via managed user edit form

---

# Testing Requirements

## General

- Run **targeted** test files for the area you changed — not necessarily the full suite.
- Use `RefreshDatabase` with **minimal** `migrateFreshUsing()` paths in feature tests (see existing tests for patterns).
- Call `$this->withoutVite()` in feature tests that hit Inertia routes.
- PHPUnit shares `RefreshDatabaseState` across classes — run conflicting test files **in isolation** if schema errors occur.

## Mandatory coverage when touching these domains

### Payments

```
tests/Feature/SellerPaymentBypassApprovalTest.php
tests/Feature/SellerCheckoutBypassApprovalTest.php  (checkout + payment overlap)
```

Assert: partial payments, order status sync, bypass metadata, admin mark paid.

### Reservations

```
tests/Feature/SellerCheckoutBypassApprovalTest.php
# Plus any reservation-specific tests in repo
```

Assert: status transitions, lock/release, conversion on payment, approval/decline.

### Remittances

```
tests/Feature/SellerRemittanceDeleteTest.php
tests/Unit/SellerRemittanceDeletePolicyTest.php
```

Assert: verify idempotency, delete policy matrix, claim pool.

### Access Control

```
tests/Feature/UserScopeOverrideFoundationTest.php
tests/Feature/UserOverrideOnlyBypassScopesPhaseATest.php
tests/Feature/DataEntryScopeEnforcementTest.php
tests/Feature/SellerScopeEnforcementTest.php
tests/Unit/RoleScopeGrantServiceTest.php
tests/Unit/AccessResolverTest.php
```

Assert: grant/revoke/inherit, locked scope rejection, 403 on revoked routes.

### User Overrides (bypass scopes)

```
tests/Feature/UserOverrideOnlyBypassScopesPhaseATest.php
tests/Feature/SellerCheckoutBypassApprovalTest.php
tests/Feature/SellerPaymentBypassApprovalTest.php
```

Assert: bypass without base scope still blocked; reason required on grant.

### User management

```
tests/Feature/AdminManagedUserUpdateTest.php
tests/Feature/AdminUserActiveStatusTest.php
```

### Exchange rates (if touching rates or intake cost display)

```
tests/Feature/ExchangeRateHistoricalFetchTest.php
tests/Feature/SellerExchangeRateAdjustmentTest.php
```

## Before deploy — minimum smoke

```bash
php artisan test tests/Feature/AdminUserActiveStatusTest.php
php artisan test tests/Feature/AdminManagedUserUpdateTest.php
# + any test file matching your changed domain
```

---

# Development Workflow

Required sequence for non-trivial changes:

```mermaid
flowchart LR
    A[1. Analyze] --> B[2. Plan]
    B --> C[3. Impact Assessment]
    C --> D[4. Implement]
    D --> E[5. Test]
    E --> F[6. QA]
    F --> G[7. Commit]
```

| Step | Actions |
|------|---------|
| **Analyze** | Read controller, service, existing tests, and `config/permissions.php` if touching access |
| **Plan** | List files to change; confirm no customer-side impact unless requested |
| **Impact Assessment** | Name protected workflows affected; list regression tests |
| **Implement** | Minimal diff; match existing naming and patterns; logic in services |
| **Test** | Add/update feature tests; run targeted `php artisan test path/to/Test.php` |
| **QA** | Manual checklist from `PROJECT_MASTER_DOCUMENTATION.md` §18 if workflow changed |
| **Commit** | Only when user requests; never `migrate:fresh`; never commit `.env` or secrets |

## PR / change description should include

- Which protected workflows are touched
- Whether customer-facing behavior changes (should be "no" unless requested)
- Migrations added (forward-only)
- Tests added or run

---

# Known Dangerous Areas

Treat edits here as **high risk** — read the full service before changing.

## Reservation conversion

- `VehicleReservationService::reserveForCustomer` vs legacy `reserve()` — two paths coexist
- First completed payment converts reservation via `PaymentService::syncOrderPaymentTotals`
- `release()` does **not** refund wallet debits
- Cron `reservations:expire` does **not** call `release()` — known gap

## PaymentService

- `syncOrderPaymentTotals` drives order status and vehicle sold state
- `allocate_on_create` behavior differs for seller checkout (false until approval)
- Changing status ranks affects entire order lifecycle display

## WalletService

- FIFO remittance consumption on debit
- Balance = credits − debits; no separate balance column
- Incorrect debit order causes allocation bugs

## InvoiceService

- Browsershot/Chrome dependency for PDF
- Reservation invoice idempotency — do not duplicate on retry
- Invoice number sequences (`KT-{n}`) must stay monotonic

## CheckoutApprovalService

- Row locks (`lockForUpdate`) on approve/decline
- Decline reverses wallet debits and soft-deletes order
- Bypass paths must still audit (`payment_data`, order notes, logs)

## Access Control

- `EffectiveScopeGrantResolver` — central to all staff enforcement
- `RoleScopeGrantService` / `UserScopeOverrideService` — validate against locked/future scopes
- Changing `config/permissions.php` route maps affects middleware 403s
- Seller enforcement intentionally omits destructive routes — do not "fix" by adding them without review

## AuctionVehicleSyncService

- Seller assignment creates draft vehicles
- Reservation materialization sets locks and seller_id
- Breaking sync leaves orphan auction cars or duplicate vehicles

## UserAccountStatusService

- Disabling user deletes sessions and Sanctum tokens
- Must remain separate from profile update (`is_active` prohibited on `UpdateManagedUserRequest`)

---

# Deployment Rules

## Migrations

```bash
# CORRECT — forward migrate only
php artisan migrate

# FORBIDDEN
php artisan migrate:fresh
php artisan migrate:fresh --seed
php artisan migrate:rollback  # avoid unless explicitly approved for this project
```

## Pre-deploy checklist

1. **Backup database** if migrations included
2. Run `php artisan migrate` on staging first
3. Run **targeted tests** for changed domains (see Testing Requirements)
4. Verify env flags: `PERMISSIONS_ENFORCE_*` (scope middleware defaults true)
5. Invoice PDF: ensure Chrome/Browsershot available on server if invoice changes deployed
6. Exchange rate API key present if intake listing deployed (`EXCHANGERATE_HOST_ACCESS_KEY` or equivalent in `config/services.php`)

## Post-deploy smoke (manual)

- Admin login → users index loads
- Disabled user cannot login
- Seller checkout → pending approval (unless bypass user)
- Customer public home loads published vehicles

## Environment notes

- Auth: Sanctum + Jetstream session + `verified` + `user.active` middleware on authenticated routes
- Scope enforcement toggles: `PERMISSIONS_ENFORCE_DATA_ENTRY`, `PERMISSIONS_ENFORCE_BIDDER`, `PERMISSIONS_ENFORCE_SHIPPING_AGENT`, `PERMISSIONS_ENFORCE_SELLER`
- Log-only permission mode: `PERMISSIONS_LOG_ONLY_ENABLED` (observability, never denies)

---

# Quick Reference

| Need | Read |
|------|------|
| Full system documentation | `/PROJECT_MASTER_DOCUMENTATION.md` |
| Lifecycle & staged intake | `docs/ARCHITECTURE.md` |
| Scope catalog & route mapping | `docs/permission-scope-catalog.md` |
| Schema appendix | `docs/system-blueprint.md` |
| Permission config | `config/permissions.php` |
| Scope UI metadata | `config/scope_manifest.php` |
| Locked scopes | `app/Support/RoleScopePolicy.php` |
| Effective grants | `app/Services/EffectiveScopeGrantResolver.php` |

---

# AI Agent Reminders

1. **Read this file** before implementing features in reservations, payments, wallet, remittances, invoices, or access control.
2. **Minimize scope** — smallest correct diff; no drive-by refactors.
3. **Do not commit** unless the user asks.
4. **Do not run** `migrate:fresh` — ever.
5. **Customer side is frozen** unless the user explicitly requests customer changes.
6. **When unsure** about financial impact, stop and list affected services + tests instead of guessing.
7. **Match existing code style** — Form Requests for validation, Services for workflows, feature tests for behavior proof.

---

*Last updated: June 2026 — align with codebase as implemented.*
