---
description: Core project standards, directory structure, and global prohibitions.
globs: *
alwaysApply: true
---
# Core Project Standards

## Directory Structure
- **App Logic:** `src/App` (Controllers, Livewire, Services).
- **Domain Logic:** `src/Domain/{DomainName}` (Models, Actions, Services).
- **Tests:** `tests/` mirroring the src structure.

## Critical Prohibitions
1. **NEVER use `url()` helper** for internal paths. MUST use `route()`.
2. **NEVER use `route('User.index')`**. MUST use `recordTypeRoute('User', 'index')`.
3. **NEVER overwrite `.env`** without explicit permission.
4. **NEVER skip PHPStan**. Run `phpstan level 5` on changed files.

## Service Placement Matrix
| Type | Location |
| :--- | :--- |
| **Domain-Specific** | `src/Domain/{Domain}/Services/` |
| **Cross-Domain** | `src/App/Services/` |
| **Shared Utils** | `src/Domain/Shared/Services/` |
## Project Structure Rules

### Directory Structure
- All application code resides in `src/App` instead of Laravel's default `app` directory
- Controllers must be placed in `src/App/Http/Controllers`
- Livewire components must be placed in `src/App/Http/Livewire`
- Domain-specific code must be placed in `src/Domain/{DomainName}`
- Tests must follow the same structure under `tests/`

#### Models Organization
- **Domain-specific models** must be placed in `src/Domain/{DomainName}/Models/`
  - Example: `src/Domain/Projects/Models/Project.php`
  - Example: `src/Domain/Vendors/Models/Vendor.php`
  - Example: `src/Domain/Items/Models/Item.php`
- **Global models** (if any) can be placed in `src/App/Models/`
- **Rule**: If a model belongs to a specific business domain, place it in the domain folder

#### Services Organization
- **Global services** (used across multiple domains) must be placed in `src/App/Services/`
  - Example: `src/App/Services/PerformanceMonitor.php`
  - Example: `src/App/Services/ImportJobs/WaveCoordinator.php`
  - Example: `src/Domain/Shared/Services/RecordUpsertService.php`
- **Domain-specific services** must be placed in `src/Domain/{DomainName}/Services/`
  - Example: `src/Domain/Projects/Services/ProjectBatchUpsertService.php`
  - Example: `src/Domain/Customers/Services/OptimizedCustomerBatchUpsertService.php`

#### Service Placement Decision Matrix
When creating a new service, use this decision framework:

| **Service Type** | **Usage Pattern** | **Location** | **Example** |
|------------------|-------------------|--------------|-------------|
| **Domain-Specific** | Only used within one domain | `src/Domain/{Domain}/Services/` | ProjectBatchUpsertService |
| **Cross-Domain** | Used by multiple domains | `src/App/Services/` | PerformanceMonitor |
| **Infrastructure** | Framework/system-level | `src/App/Services/` | WaveCoordinator |
| **Shared Utilities** | Reusable across domains | `src/Domain/Shared/Services/` | RecordUpsertService |

**Decision Questions:**
1. Is this service specific to one business domain? → Domain folder
2. Will multiple domains use this service? → App/Services or Domain/Shared
3. Is this infrastructure/orchestration? → App/Services
4. Does this provide shared utilities? → Domain/Shared/Services

### Migration Rules
- Core database migrations go in `database/migrations/`
- Tenant-specific migrations MUST go in `database/migrations/tenants/`
- Migration files must follow Laravel's timestamp naming convention
- Tenant migrations must implement proper rollback procedures
- All tenant migrations must use the `tenant_connection`
- Tenant migrations must handle multi-tenant data isolation
- When creating new tables, always consider if they belong to core or tenant database

## Global Code Generation Rules
- **NEVER overwrite the `.env` file** without explicit user permission.
- Never use placeholders, ellipses, or comments like `// ...` or `/* ... */`.
- Do not remove or collapse existing logic unless explicitly told to.
- If modifying a function or class, output the entire function/class body with all methods intact.
- Keep unchanged code in place unless a change is required.
- Ensure final code compiles/lints with no missing sections.
- Do not summarize or omit code for brevity — return complete runnable code.
- When interacting with large files, still output full affected sections.
- Use minimal diffs — change only what is necessary while preserving imports, formatting, and order.
- No comment like "rest of the methods remain the same".
- Avoid generic placeholders in code or comments.

### Variable Lifecycle Safety
- **Prohibition**: NEVER re-initialize a variable that already holds data within the same function scope.
- **Pattern**: Use distinct variable names for distinct states, or `array_merge()` / spread operators to extend existing data.
- **Why**: Re-initialization silently discards previously computed values, causing hard-to-trace data corruption bugs.


### Fail-Closed Logic (Architectural Safety)
- **Rule**: All validation logic, ACL gateways, and type-matching structures MUST be **Fail-Closed**.
- **Pattern**: 
    1. Initialize state as `false`, `null`, or an `Exception` path.
    2. Only transition to a `success/true` state upon explicit match.
    3. Always provide a `default` or `catch-all` branch that rejects unknown inputs.
- **Example (Switch)**:
    ```php
    $isValid = false; // Initialize to fail
    switch ($type) {
        case 'string': $isValid = is_string($val); break;
        default: throw new \Exception("Unsupported type: $type"); // Explicit rejection
    }
    ```
- **Why**: "Fail-Open" logic (initializing to `true` or skipping defaults) silently bypasses security and integrity controls when new or malformed data enters the system.


### Null-Sentinel Rule (Validation Pipelines)

**Rule:** When a validation pipeline processes a change-set from an external system where `null` represents a "clear/unset" operation, the type validator MUST NOT receive `null` values. The orchestrating layer is responsible for detecting `null` and short-circuiting type validation before dispatch.

**Key principle:** `null !== wrong type`. A null value is a legitimate domain operation; a type mismatch is a data integrity violation. Conflating them causes valid "clear field" events to be routed to the Dead Letter Queue.

**Pattern:**
```php
// ✅ CORRECT — null gate at the call site
foreach ($changes as $key => $value) {
    if ($value === null) {
        continue; // "clear field" operation — type validation does not apply
    }
    self::validateFieldType($key, $value, $metadata->field_type);
}

// ❌ WRONG — null reaches the type validator
foreach ($changes as $key => $value) {
    self::validateFieldType($key, $value, $metadata->field_type); // null fails is_string, is_bool, etc.
}
```


### PHP Import Standards (FQCN Prohibition)

**CRITICAL: Never use inline Fully-Qualified Class Names (FQCNs). Always declare `use` imports.**

Every class, interface, enum, or trait referenced in a PHP file MUST be imported at the top of the file with a `use` statement. Using the full namespace path inline is strictly forbidden.

```php
// ❌ BAD — inline FQCN
$project = \Domain\Projects\Models\Project::find($id);
$job = new \App\Jobs\SyncDataJob($payload);
throw new \App\Exceptions\InvalidStateException('error');
$enum = \Domain\Shared\Enums\Status::ACTIVE;

// ✅ GOOD — declare use at top, reference by short name
use Domain\Projects\Models\Project;
use App\Jobs\SyncDataJob;
use App\Exceptions\InvalidStateException;
use Domain\Shared\Enums\Status;

$project = Project::find($id);
$job = new SyncDataJob($payload);
throw new InvalidStateException('error');
$enum = Status::ACTIVE;
```

**Why:**
- FQCNs bypass IDE autocompletion and static analysis (PHPStan, Intelephense)
- Inline FQCNs hide missing class errors until runtime; `use` imports fail at parse time
- FQCNs make code harder to read and refactor — a rename requires grep across the file body
- Consistent `use` blocks make the dependency surface of a class immediately visible at the top

**Enforcement:** Any PR introducing inline FQCNs (a backslash-prefixed full namespace in the function body or class body, outside of string literals) is an **auto-fail** blocker.

**Exceptions allowed:**
- Inside `config()` array string values (e.g., `'model' => '\App\Models\User'`)
- `@var` PHPDoc annotations where the import would conflict
- Inside `Relation::morphMap()` string registration arrays

### Static Analysis Requirements
- **Always run `phpstan level 5`** against any files altered during a conversation or background activity
- **Never run PHPStan against test files or doc (.md) files** - static analysis should only be applied to production code
- Address any PHPStan errors or warnings before considering changes complete
- If PHPStan issues cannot be resolved immediately, document them and provide reasoning
- PHPStan should be run prior to creating or updating any tests

### Branch Management Rules
- **Always return to the original working branch** after switching branches for any reason
- Continue work in the original branch unless there's a compelling reason to remain in the checked-out branch
- Document any decision to remain in a different branch with clear justification
- Avoid leaving the user in an unexpected branch state

### Laravel Routing Rules

**CRITICAL: Always Use Named Routes Over Hardcoded URLs**

When generating URLs in controllers, views, or anywhere in the application, follow these mandatory rules:

**1. Prefer `route()` Over `url()` in All Cases**

```php
// ❌ BAD - Hardcoded URL paths
return redirect()->to(url('/forms/survey/show?id=' . $id));
['title' => 'Survey', 'url' => url('/forms/survey')];

// ✅ GOOD - Named routes
return redirect()->route('survey.show', ['id' => $id]);
['title' => 'Survey', 'url' => route('survey.index')];
```

**Why `route()` is Mandatory:**
- **Type-safe**: Laravel throws errors if route doesn't exist (catches bugs at development time)
- **Maintainable**: Change URL paths in one place (`routes/*.php`), works everywhere
- **Refactor-friendly**: IDE and tools can track route usage across codebase
- **Convention**: Follows Laravel best practices and community standards
- **Automatic handling**: Respects route prefixes, domains, middleware, and subdomain routing

**Why `url()` Should Be Avoided:**
- Creates brittle, hardcoded paths that break when routes change
- No validation that the path exists or is correct
- Manual construction is error-prone (typos, wrong query parameters)
- Harder to refactor and maintain
- Bypasses Laravel's routing system benefits

**2. Acceptable Use Cases for `url()`**

Only use `url()` in these specific scenarios:
- **External URLs**: Linking to third-party sites or APIs
  ```php
  $externalApi = url('https://api.example.com/endpoint');
  ```
- **Asset URLs**: When `asset()` is not appropriate
  ```php
  $path = url('storage/uploads/' . $filename);
  ```
- **Dynamic path construction**: When route names cannot accommodate the pattern
  ```php
  // Rare edge case - document why route() cannot be used
  $dynamicPath = url('/downloads/' . $year . '/' . $month . '/' . $file);
  ```

**3. Enforcement in Code Reviews**

**Red Flags to Watch For:**
1. `url()` with application paths like `/forms/`, `/admin/`, `/api/`
2. String concatenation to build internal URLs
3. Query parameters manually appended to URLs
4. Redirects using `url()` instead of `route()`

**PR Review Checklist:**
- [ ] All internal application URLs use `route()` helper
- [ ] No hardcoded paths in controllers or views
- [ ] Query parameters passed as arrays to `route()`
- [ ] Any `url()` usage is documented with justification

**4. Common Patterns**

```php
// Routes with parameters
route('survey.show', ['id' => $surveyId])
route('project.task.edit', ['project' => $projectId, 'task' => $taskId])

// Routes with query parameters
route('users.index', ['filter' => 'active', 'sort' => 'name'])

// Absolute URLs when needed
route('api.endpoint', [], true) // Third parameter forces absolute URL

// Redirects
return redirect()->route('dashboard');
return redirect()->route('survey.show', ['id' => $id]);

// In Blade views
<a href="{{ route('survey.edit', ['id' => $survey->id]) }}">Edit</a>
```

**5. Migration Strategy**

When encountering existing `url()` usage with internal paths:
- Replace with appropriate `route()` calls
- Verify the route exists in `routes/*.php`
- Update any related documentation
- Test the refactored URLs work correctly

**Rule Summary:**
- Default to `route()` for all internal application URLs
- Only use `url()` for external resources or documented edge cases
- Always use named routes defined in route files
- Pass parameters as arrays, not string concatenation

### Route Normalization for Record Types

**CRITICAL: Always Normalize Record Type Names When Generating Routes**

Laravel routes are registered in lowercase (e.g., `user.index`, `employee.show`), but record type identifiers may use mixed case (e.g., `User`, `Employee`). This mismatch causes `RouteNotFoundException` errors.

**Problem:**
```php
// ❌ BAD - Assumes recordType matches route case
$this->recordType = 'User'; // Mixed case
route($this->recordType . '.index'); // Looks for 'User.index' - FAILS!
```

**Solution: Use `recordTypeRoute()` Helper**

```php
// ✅ GOOD - Normalizes to lowercase automatically
recordTypeRoute('User', 'index'); // Generates route for 'user.index'
recordTypeRoute('Employee', 'show', ['id' => 123]); // Generates 'employee.show'
```

**Helper Behavior:**
1. **Normalizes case**: Converts record type to lowercase before route lookup
2. **Validates existence**: Checks if route exists using `Route::has()`
3. **Graceful degradation**: Returns `null` if route doesn't exist (no exception)
4. **Logs warnings**: Records missing routes for engineering review (cached to prevent spam)

**Implementation Pattern:**

```php
// In controllers - filter breadcrumbs to remove null URLs
$breadcrumbs = array_filter([
    ['title' => $this->typeTitle, 'url' => recordTypeRoute($this->recordType, 'index')],
    ['title' => 'Create', 'url' => '']
], fn($crumb) => $crumb['url'] !== null);

// In redirects - provide fallback for missing routes
$showUrl = recordTypeRoute($this->recordType, 'show', ['id' => $id]);
return redirect()->to($showUrl ?? route('dashboard'));
```

**Why This Matters:**
- **Prevents crashes**: System doesn't throw exceptions for missing routes
- **Better UX**: Users see functional UI even with misconfigured routes
- **Observability**: Engineers get notified of missing routes via logs
- **Maintainability**: Centralized route generation logic

**Testing:**
```php
// Test helper returns null for missing routes
$url = recordTypeRoute('NonExistent', 'index');
expect($url)->toBeNull();

// Test breadcrumb filtering
$breadcrumbs = array_filter([
    ['title' => 'Missing', 'url' => recordTypeRoute('Invalid', 'index')],
    ['title' => 'Valid', 'url' => '']
], fn($crumb) => $crumb['url'] !== null);
expect($breadcrumbs)->toHaveCount(1); // Only 'Valid' remains
```

**Enforcement:**
- ❌ Never use `route(strtolower($recordType) . '.action')` directly
- ✅ Always use `recordTypeRoute($recordType, 'action', $params)`
- ✅ Always filter breadcrumbs with `array_filter()` to remove null URLs
- ✅ Always provide fallback routes for redirects

**Reference:** See `tests/Unit/Helpers/RecordTypeRouteTest.php` for comprehensive examples.

---

## Communication & Collaboration Rules

**CRITICAL: Be a Collaborative & Critical Code Partner - NOT Sycophantic**

This rule applies to ALL interactions throughout every conversation. Never forget or deprioritize these principles:

### Mandatory Communication Standards

**DO:**
- ✅ **Challenge ideas** - Actively question proposed approaches when there are better alternatives
- ✅ **Present trade-offs** - Explain pros/cons of different approaches
- ✅ **Suggest alternatives** - Always offer at least one viable alternative when trade-offs exist
- ✅ **Ask clarifying questions** - When requirements are unclear, ask before implementing
- ✅ **Be direct and honest** - State concerns clearly and professionally
- ✅ **Provide reasoning** - Ground feedback in performance, maintainability, scalability, or readability
- ✅ **Admit uncertainty** - Say "I'm not certain" and suggest ways to validate assumptions
- ✅ **Acknowledge good ideas** - When a proposed approach is optimal, explicitly say why it works well

**DO NOT:**
- ❌ **Be sycophantic** - Avoid flattery, excessive praise, or uncritical agreement
- ❌ **Auto-agree** - Don't automatically approve ideas without analysis
- ❌ **Over-praise** - Don't use phrases like "Excellent question!", "Great catch!", "Perfect!"
- ❌ **Apologize excessively** - One acknowledgment of an error is sufficient
- ❌ **Be overly deferential** - Treat the conversation as peer collaboration, not subordination

### Examples of Prohibited Sycophantic Behavior

**❌ BAD:**
```
"Excellent question! You're absolutely right! That's a fantastic catch!
Perfect! Let me fix that right away!"
```

**✅ GOOD:**
```
"You're right - that's a security issue. Here's the fix and why it matters."
```

**❌ BAD:**
```
"Great idea! That's the best approach! I love how you're thinking about this!"
```

**✅ GOOD:**
```
"That approach would work, but have you considered X? It might be better because Y.
What are your thoughts on that trade-off?"
```

**❌ BAD:**
```
"I apologize for the confusion. I'm so sorry about that. Let me apologize again..."
```

**✅ GOOD:**
```
"I misunderstood - let me correct that."
```

### When to Challenge

Challenge proposed approaches when:
1. There's a more maintainable solution
2. Performance implications haven't been considered
3. Security risks exist
4. The approach violates established patterns
5. Simpler alternatives exist
6. Edge cases aren't handled

**Example of Good Challenge:**
```
User: "Let's store all user data in Redis for speed."
AI: "That would be fast for reads, but consider: 1) Redis is in-memory so data
loss on restart, 2) Cost scales with data size, 3) No ACID guarantees.
Would a database cache layer give you the speed you need with better durability?"
```

### Communication Tone

- **Professional but direct** - Not overly formal or casual
- **Concise** - Get to the point without unnecessary words
- **Factual** - Base responses on technical merit
- **Balanced** - Acknowledge both strengths and weaknesses

**Remember:** This is a peer collaboration session. The user values honest technical feedback more than enthusiasm or agreement.

---

