PLA Project Kickoff
Reference guide - not a scaffolder. Read and follow step by step.
Audience: AI agents (Claude Code)
Gold-standard project: mentat (mentat/ in the workspace root)
Prerequisite: The mentat project must be cloned in the workspace root. If not present, clone it:
git clone git@gitlab.com:publicala/mentat.git
Reference files
All baseline configs live in references/. Copy and adapt as needed.
| File | Purpose |
|---|---|
references/composer-scripts.json | Composer scripts block (setup, dev, lint, test) |
references/package.json | npm scripts + devDependencies baseline |
references/app.css | Tailwind v4 entry with Flux imports |
references/vite.config.js | Vite + Tailwind + Laravel plugin |
references/lefthook.yml | Pre-commit (pint, rector, prettier) + pre-push (phpstan) |
references/session-start.sh | Claude Code SessionStart hook for lefthook |
references/claude-settings.json | Claude Code hooks config |
references/Pest.php | Pest global config (fakes, freeze, refresh) |
references/claude-md-template.md | Root CLAUDE.md template |
references/wide-events.md | Observability wide events guide |
1. Stack
| Component | Version / Tool | Notes |
|---|---|---|
| Framework | Laravel 12 | laravel/framework ^12.x |
| PHP | 8.4 | declare(strict_types=1) everywhere |
| Frontend | Livewire 4 SFCs | Single File Components only |
| UI Kit | Flux Pro | Key: 32e360d4-88f9-436f-964d-87babb03a44c |
| CSS | Tailwind v4 | Via @tailwindcss/vite plugin |
| DB (local) | SQLite | Zero-config local dev |
| DB (cloud) | MySQL 8 | Laravel Cloud managed |
| Testing | Pest v4 | Always it(), never class-based |
| AI | laravel/ai | Official Laravel AI SDK |
| Monitoring | Nightwatch | laravel/nightwatch |
| Build | Vite 7.x | With @tailwindcss/vite + laravel-vite-plugin |
Flux Pro requires a Composer repository and auth.json:
// composer.json repositories
[{ "name": "flux-pro", "type": "composer", "url": "https://composer.fluxui.dev" }]
Reference: mentat's composer.json, package.json, vite.config.js
2. Prerequisites
The fastest way to get PHP, Composer, Node, npm, and the Laravel installer:
# Installs PHP 8.4, Composer, Node, npm, and the Laravel installer in one step
/bin/bash -c "$(curl -fsSL https://php.new/install/mac/8.4)"
Additional tools:
- Lefthook -
brew install lefthook - Laravel Cloud CLI -
composer global require laravel/cloud-cli
3. Project Bootstrap
Step-by-step guide. Agent reads and follows manually - not automated.
3.1 Create Laravel project
composer create-project laravel/laravel {project-name}
cd {project-name}
3.2 composer.json
Add Flux Pro repo, core packages, and scripts.
Required packages:
laravel/ai, laravel/framework, laravel/horizon, laravel/nightwatch,
livewire/flux-pro, livewire/livewire, nunomaduro/essentials
Dev packages:
driftingly/rector-laravel, larastan/larastan, laravel/pail, laravel/pint,
pestphp/pest, pestphp/pest-plugin-browser, pestphp/pest-plugin-laravel,
pestphp/pest-plugin-type-coverage, rector/rector
Composer scripts: Copy from references/composer-scripts.json into composer.json scripts block and adapt the dev concurrently command for your project's services.
Best practice: composer setup bootstraps everything, composer dev runs all services via concurrently.
3.3 package.json
Copy baseline from references/package.json and adapt versions as needed.
3.4 Config files
Copy these from mentat and adapt:
| File | Source path | Purpose |
|---|---|---|
pint.json | mentat/pint.json | PHP formatting (strict rules, etc.) |
rector.php | mentat/rector.php | PHP transforms (dead code, early return, strict booleans) |
phpstan.neon | mentat/phpstan.neon | Static analysis level 8 |
lefthook.yml | references/lefthook.yml | Git hooks (pre-commit + pre-push) |
.editorconfig | mentat/.editorconfig | Editor settings (utf-8, lf, 4 spaces) |
config/livewire.php | mentat/config/livewire.php | SFC mode, page locations |
config/essentials.php | mentat/config/essentials.php | Eloquent unguard |
3.5 Tailwind v4 CSS
Copy references/app.css to resources/css/app.css.
3.6 Vite config
Copy references/vite.config.js to project root.
3.7 .env.example
Set SQLite defaults for local development:
DB_CONNECTION=sqlite
3.8 CLAUDE.md
Create root + directory-level CLAUDE.md files. Use template at references/claude-md-template.md.
3.9 Claude Code Hooks
Set up a SessionStart hook to ensure lefthook git hooks are active in every Claude Code session.
- Copy
references/session-start.shto.claude/hooks/session-start.sh - Make it executable:
chmod +x .claude/hooks/session-start.sh - Copy
references/claude-settings.jsonto.claude/settings.json
This is fast, idempotent, and warns gracefully if lefthook is missing. It guarantees that pre-commit (pint + rector + prettier) and pre-push (phpstan) hooks are active before any commits happen.
3.10 Laravel Cloud Setup
cloud auth # authenticate (one-time)
cloud repo:config # save project/org defaults (per project)
4. Code Conventions
High-level principles. See mentat's pint.json and rector.php for enforcement.
declare(strict_types=1)in every PHP file- Enums: string-backed, names describe values
- Models:
casts()method, ULIDs viaHasUlids - Services: injected via constructor
- Jobs: implement
ShouldQueue - Value objects:
readonlyclasses - Frontend: Livewire 4 SFCs only (no class-based components), placed in
resources/views/pages/ - Flux Pro for all UI components - never hand-roll what Flux provides
- Strict comparisons (
===) everywhere DateTimeImmutableoverDateTime- No superfluous elseif/else - use early returns
- Ordered class elements: traits, cases, constants, properties, construct, public methods, protected, private
Reference: mentat's pint.json, rector.php, tests/Arch/
Laravel-First Conventions
Always prefer Laravel utilities over native PHP equivalents. This is a hard rule for consistency across all PLA projects.
Filesystem - Use the File facade, never native functions:
| Instead of | Use |
|---|---|
is_file($path), file_exists($path) | File::exists($path) |
is_dir($path) | File::isDirectory($path) |
file_get_contents($path) | File::get($path) |
file_put_contents($path, $data) | File::put($path, $data) |
mkdir($path, 0755, true) | File::ensureDirectoryExists($path) |
rmdir($path) / unlink($path) | File::deleteDirectory($path) / File::delete($path) |
Collections - Use collect() pipelines, never nested array functions:
// Bad
$result = array_values(array_filter(array_map($fn, $items)));
// Good
$result = collect($items)->map($fn)->filter()->values()->all();
Prefer semantic methods: ->reject() over ->filter() with negation, ->contains() over in_array(), ->pluck() over array_column(), ->keys() over array_keys().
Strings - Use Str:: helpers and Str::of() fluent API:
| Instead of | Use |
|---|---|
str_starts_with($s, $p) | Str::startsWith($s, $p) |
str_contains($s, $p) | Str::contains($s, $p) |
str_ends_with($s, $p) | Str::endsWith($s, $p) |
mb_substr($s, 0, $pos) / mb_strpos parsing | Str::before($s, ':') / Str::after($s, ':') |
explode + array_map + array_filter | Str::of($s)->explode("\n")->map(...)->filter() |
Arr helpers - Use Arr:: for associative array operations:
| Instead of | Use |
|---|---|
isset($arr['a']['b']) ? $arr['a']['b'] : null | Arr::get($arr, 'a.b') |
array_key_exists($k, $arr) | Arr::has($arr, $k) |
PHPStan note: Collection::values()->all() returns array<int, T>, not list<T>. Add /** @var list<T> */ above the return when the method's return type is list<T>.
5. Standard Directory Structure
app/
Actions/ # Single-purpose action classes
Ai/ # AI agents, tools, middleware
Agents/
Middleware/
Tools/
Console/ # Artisan commands
Enums/ # String-backed enums
Exceptions/ # Custom exceptions
Http/ # Controllers, middleware, requests
Jobs/ # Queued jobs (ShouldQueue)
Models/ # Eloquent models (HasUlids)
Providers/ # Service providers
Services/ # Business logic services
ValueObjects/ # Readonly value objects
config/
ai.php # AI provider configuration
livewire.php # Livewire SFC settings
essentials.php # Eloquent unguard, etc.
database/
factories/
migrations/
seeders/
docs/
architecture/ # System design docs
patterns/ # Implementation patterns
plans/ # Development plans
resources/
css/ # Tailwind v4 entry
js/ # JS entry points
views/
components/ # Reusable Blade/Livewire components
layouts/ # Layout templates
pages/ # Livewire SFC page components
tests/
Arch/ # Architecture tests (Pest arch())
Browser/ # Playwright browser tests
Feature/ # Integration tests
Unit/ # Pure logic tests
fixtures/ # Test data
Pest.php # Global test config
stubs/
agent.stub # AI agent template
tool.stub # AI tool template
structured-agent.stub # Structured output agent template
middleware.stub # AI middleware template
6. Testing
Framework
Pest v4. Always it() syntax, never class-based PHPUnit tests.
Organization
| Type | Location | When to Use |
|---|---|---|
| Arch | tests/Arch/ | Code quality, conventions, layer deps |
| Unit | tests/Unit/ | Pure logic, no DB or framework |
| Feature | tests/Feature/ | Livewire, HTTP, jobs, services |
| Browser | tests/Browser/ | Full-stack rendering via Playwright |
Non-negotiable quality gates
- 100% type coverage (
pest --type-coverage --min=100) - PHPStan level 8
- Pint clean (no formatting violations)
- Rector clean (no transform violations)
Pest.php global config
Copy references/Pest.php to tests/Pest.php and add necessary imports. Key behaviors: RefreshDatabase, preventStrayRequests, preventStrayProcesses, Sleep::fake, freezeTime.
Standard arch tests
Create these in tests/Arch/. Reference: mentat's tests/Arch/ (13 test files).
| Test file | What it enforces |
|---|---|
CodeQualityTest.php | declare(strict_types=1) |
TestStyleTest.php | it() syntax only, no class-based tests |
EnumsTest.php | String-backed, naming conventions |
ServicesTest.php | Final, no Eloquent dependencies |
JobsTest.php | Final, ShouldQueue |
ModelsTest.php | Final, HasUlids, casts() |
LivewireSfcTest.php | SFCs only, in correct directories |
ObservabilityTest.php | No Log::debug(), namespaced context keys |
LayerDependenciesTest.php | Channels don't depend on Jobs, Enums don't depend on Models, etc. |
AgentToolsTest.php | Tools implement correct interface |
AiMiddlewareTest.php | Middleware structure |
ChannelsTest.php | Channel conventions |
NumberFormattingTest.php | Consistent number formatting |
Reference: mentat's tests/CLAUDE.md, tests/Pest.php, tests/Arch/
7. Linting & Code Quality
| Tool | Config file | Purpose |
|---|---|---|
| Pint | pint.json | PHP formatting (Laravel preset + strict rules) |
| Rector | rector.php | PHP transforms (dead code, early return, strict booleans) |
| Prettier | package.json scripts | JS/CSS formatting + Tailwind class sorting |
| PHPStan | phpstan.neon | Static analysis level 8 with Larastan |
| Lefthook | lefthook.yml | Git hooks |
Lefthook pattern
Copy baseline from references/lefthook.yml. Key: stage_fixed: true auto-stages corrected files.
Composer scripts pattern
composer lint # Fix: rector + pint + prettier
composer test:lint # Check: pint --test + rector --dry-run + prettier --check
composer test:types # PHPStan
composer test:type-coverage # Pest type coverage (min 100%)
composer test:unit # Pest parallel with coverage
composer test # All of the above
Reference: mentat's config files
8. Git Workflow
- Branch from
main:feature/{description} - Lowercase hyphen-separated branch names
- MR titles reference Linear issue (e.g.
DEV-123: Add user export) - Use Lefthook hooks - never skip with
--no-verify
9. AI Integration (laravel/ai)
All AI code lives in app/Ai/. Four patterns, each with a mentat stub:
| Pattern | Interfaces / Traits | Key Methods | Stub |
|---|---|---|---|
| Conversational Agent | Agent, Conversational, HasTools + Promptable | instructions(), messages(), tools() | stubs/agent.stub |
| Structured Output Agent | + HasStructuredOutput | + schema(JsonSchema) | stubs/structured-agent.stub |
| Tool | Tool | description(), handle(Request), schema(JsonSchema) | stubs/tool.stub |
| Middleware | (pipeline class) | handle(AgentPrompt, Closure) → $next()->then(fn) | stubs/middleware.stub |
Config
config/ai.php - Multi-provider configuration. Default provider is OpenAI. Supports Anthropic, Azure, Gemini, Groq, Mistral, Ollama, OpenRouter, and more.
Testing AI agents
MyAgent::fake(['response text']);
MyClassifier::fake([['category' => 'product_docs']]);
Reference: mentat's app/Ai/, stubs/, config/ai.php
10. Deployment
Platform
Laravel Cloud (mandatory for all new projects).
CLI workflow
composer global require laravel/cloud-cli
cloud auth
cloud repo:config # Save app/org defaults (run once)
cloud deploy # Deploy
cloud deploy:monitor # Watch deploy progress
Database
MySQL 8 on Laravel Cloud.
CI (referenced, not enforced)
GitLab CI with stages: build-image, build, test, qa, deploy.
Post-deploy verification
- Monitor pipeline completion (use
/gitlabskill) - After deploy succeeds, wait 3-5 min for Laravel Cloud rollout
- Use Nightwatch MCP
list_issuesto check for new issues since deploy - Zero new issues after 5 min = green signal
- If issues found:
get_issue()for stack trace + context, read source, fix, repeat
11. Observability
Brief summary - see references/wide-events.md for full guide.
- Wide events pattern: one canonical
Log::info()per unit of work - Context:
Context::add('{project}.key', value)with project-namespaced keys - Warnings: must include
reasonfield - No breadcrumbs: no
Log::debug(), no step-by-step narration - Queue failures: centralized in global
Queue::failinglistener - Nightwatch: monitors production, surfaces context in job/exception records
12. CLAUDE.md Practice
Every project needs root + directory-level CLAUDE.md files.
Root CLAUDE.md
Covers: overview, stack, architecture, key commands, env vars, observability, deploy workflow.
Template: references/claude-md-template.md
Directory-level CLAUDE.md
Add when patterns are non-obvious. Focus on what won't change: architectural patterns, conventions, and the why behind complex or confusing decisions. These files should help an agent (or a new contributor) understand the reasoning that isn't self-evident from reading the code.
Include:
- Structural patterns (how things are built, what to follow when adding new ones)
- Key decisions with rationale (the "why," not just the "what")
- Non-obvious constraints or trade-offs
Do NOT include:
- Lists or tables of current implementations (maintenance burden; the code is the source of truth)
- Anything derivable by reading the files in the directory
- Ephemeral details that change with each PR
| Location | Content |
|---|---|
tests/CLAUDE.md | Test types, running commands, factory patterns, faking conventions |
app/Ai/CLAUDE.md | Agent patterns, tool interface, testing fakes |
app/Models/CLAUDE.md | Model conventions, relationships, casts |
app/Jobs/CLAUDE.md | Job patterns, observability requirements |
app/Health/Checks/CLAUDE.md | Check pattern, how to add new checks, key decisions with rationale |
docs/CLAUDE.md | Documentation structure, where to find what |
13. Quick Reference Checklist
Verify each item exists and matches mentat's patterns:
| Item | Config / File | Mentat source |
|---|---|---|
| PHP 8.4 + strict types | composer.json, pint.json | mentat/composer.json |
| Laravel 12 | composer.json | mentat/composer.json |
| Livewire 4 SFCs | config/livewire.php | mentat/config/livewire.php |
| Flux Pro repo | composer.json repositories | mentat/composer.json |
| Tailwind v4 | resources/css/app.css | mentat/resources/css/app.css |
| Vite 7.x config | vite.config.js | mentat/vite.config.js |
| Pest v4 | composer.json require-dev | mentat/composer.json |
| Pint (Laravel preset + strict) | pint.json | mentat/pint.json |
| Rector (Laravel + strict) | rector.php | mentat/rector.php |
| PHPStan level 8 | phpstan.neon | mentat/phpstan.neon |
| Lefthook hooks | lefthook.yml | mentat/lefthook.yml |
| EditorConfig | .editorconfig | mentat/.editorconfig |
| Prettier + plugins | package.json | mentat/package.json |
| Essentials config | config/essentials.php | mentat/config/essentials.php |
| AI config | config/ai.php | mentat/config/ai.php |
| Nightwatch | composer.json require | mentat/composer.json |
| Composer scripts (setup, dev, lint, test) | composer.json scripts | mentat/composer.json |
| npm scripts (build, dev, lint) | package.json scripts | mentat/package.json |
| SQLite local default | .env.example | mentat/.env.example |
| Root CLAUDE.md | CLAUDE.md | mentat/CLAUDE.md |
| Tests CLAUDE.md | tests/CLAUDE.md | mentat/tests/CLAUDE.md |
| Claude Code SessionStart hook | .claude/hooks/session-start.sh | Section 3.9 |
| Arch tests | tests/Arch/ | mentat/tests/Arch/ |
| Pest.php global config | tests/Pest.php | mentat/tests/Pest.php |
| AI stubs | stubs/ | mentat/stubs/ |