docs: sync AGENTS.md with orchestration repo updates
This commit is contained in:
parent
044b94b080
commit
2a23f0d210
192
AGENTS.md
192
AGENTS.md
|
|
@ -20,7 +20,7 @@ ## General
|
|||
- There should be Button in the Top Bar, to refresh the Data from the CTS API and a timestamp with the latest refresh.
|
||||
- LoggedIn User should be visible in the Top Bar
|
||||
- every action should be immediately persistent, no separate "save" button required, unless explicitly described.
|
||||
- parser and generator of song files (.pro) are added later, to just add simple placeholder and wait for the detailed spec to implement
|
||||
- ProPresenter `.pro` file parser/generator is implemented as a separate composer package (`propresenter/parser`) linked via path repository
|
||||
|
||||
## The Plan
|
||||
|
||||
|
|
@ -98,112 +98,160 @@ ## SongDB Import
|
|||
|
||||
---
|
||||
|
||||
## Repository Structure
|
||||
|
||||
Two git repositories, both local (no remote):
|
||||
|
||||
| Repo | Path | Branch | Purpose |
|
||||
|------|------|--------|---------|
|
||||
| **cts-work** | `/Users/thorsten/AI/cts-work` | `cts-presenter-app` | Laravel app (main codebase) |
|
||||
| **propresenter-work** | `/Users/thorsten/AI/propresenter-work/php` | `propresenter-parser` | ProPresenter .pro/.proplaylist parser (composer path dependency) |
|
||||
|
||||
The parser is linked via `composer.json` path repository: `"url": "../propresenter-work/php"`.
|
||||
|
||||
## Build, Test, Lint Commands
|
||||
|
||||
```bash
|
||||
# Setup (first time)
|
||||
composer setup
|
||||
### cts-work (Laravel App)
|
||||
|
||||
# Dev server (Laravel + Vite + Queue + Logs via concurrently)
|
||||
```bash
|
||||
# First-time setup
|
||||
composer setup # install, .env, key:generate, migrate, npm install, npm build
|
||||
|
||||
# Dev server (Laravel + Vite + Queue + Logs)
|
||||
composer dev
|
||||
|
||||
# Build frontend
|
||||
npm run build
|
||||
|
||||
# Run all PHP tests (clears config cache first)
|
||||
# Run ALL PHP tests (206 tests, clears config cache first)
|
||||
composer test
|
||||
# or directly:
|
||||
php artisan test
|
||||
|
||||
# Run a single PHP test file
|
||||
# Single test file
|
||||
php artisan test tests/Feature/ServiceControllerTest.php
|
||||
|
||||
# Run a single test method
|
||||
# Single test method
|
||||
php artisan test --filter=test_service_kann_abgeschlossen_werden
|
||||
|
||||
# Run only Unit or Feature suite
|
||||
php artisan test --testsuite=Unit
|
||||
# Test suite
|
||||
php artisan test --testsuite=Feature
|
||||
php artisan test --testsuite=Unit
|
||||
|
||||
# PHP code formatting (Laravel Pint - default Laravel preset)
|
||||
# PHP formatting (Laravel Pint, default preset — no pint.json)
|
||||
./vendor/bin/pint
|
||||
# Check only (no changes):
|
||||
./vendor/bin/pint --test
|
||||
./vendor/bin/pint --test # check only
|
||||
|
||||
# Run e2e tests (requires running dev server at http://cts-work.test)
|
||||
# E2E tests (requires dev server at http://cts-work.test)
|
||||
npx playwright test
|
||||
# Single e2e file:
|
||||
npx playwright test tests/e2e/service-list.spec.ts
|
||||
|
||||
# Migrations
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
### propresenter-work (Parser Module)
|
||||
|
||||
```
|
||||
app/
|
||||
Http/Controllers/ # Inertia controllers, return Inertia::render() or JSON
|
||||
Http/Requests/ # Form request validation
|
||||
Http/Middleware/ # HandleInertiaRequests shares props
|
||||
Models/ # Eloquent models with factories in database/factories/
|
||||
Services/ # Business logic (ChurchToolsService, SongService, etc.)
|
||||
Jobs/ # Queue jobs (PowerPoint conversion)
|
||||
Mail/ # Mailable classes
|
||||
Cts/ # CTS API spike/sync utilities
|
||||
resources/js/
|
||||
Pages/ # Vue page components (mapped via Inertia::render)
|
||||
Components/ # Reusable Vue components
|
||||
Composables/ # Vue composables (useAutoSave)
|
||||
Layouts/ # AuthenticatedLayout, GuestLayout, MainLayout
|
||||
tests/
|
||||
Feature/ # HTTP/integration tests (class-based, PHPUnit style)
|
||||
Unit/ # Unit tests
|
||||
e2e/ # Playwright browser tests (TypeScript)
|
||||
```bash
|
||||
cd /Users/thorsten/AI/propresenter-work/php
|
||||
|
||||
# Run all tests (230 tests)
|
||||
./vendor/bin/phpunit
|
||||
|
||||
# Single test file
|
||||
./vendor/bin/phpunit tests/ProFileReaderTest.php
|
||||
```
|
||||
|
||||
## Code Style — PHP
|
||||
## Architecture
|
||||
|
||||
```
|
||||
cts-work/
|
||||
app/Http/Controllers/ # Inertia controllers (Inertia::render or JSON)
|
||||
app/Models/ # Eloquent models (factories in database/factories/)
|
||||
app/Services/ # Business logic (ChurchToolsService, ProExportService, etc.)
|
||||
app/Jobs/ # Queue jobs (PowerPoint conversion)
|
||||
app/Mail/ # Mailable classes (German content)
|
||||
resources/js/Pages/ # Vue page components (mapped via Inertia::render)
|
||||
resources/js/Components/ # Reusable Vue components
|
||||
resources/js/Composables/ # Vue composables (useAutoSave)
|
||||
resources/js/Layouts/ # AuthenticatedLayout, GuestLayout
|
||||
tests/Feature/ # Pest v4 / PHPUnit feature tests
|
||||
tests/e2e/ # Playwright browser tests (TypeScript)
|
||||
|
||||
propresenter-work/php/
|
||||
src/ # ProFileReader, ProFileGenerator, ProPlaylistGenerator, Song, Group, Slide, Arrangement
|
||||
tests/ # PHPUnit 11 tests with #[Test] attributes
|
||||
ref/ # .pro fixture files for testing
|
||||
```
|
||||
|
||||
## Code Style -- PHP
|
||||
|
||||
- **Formatter**: Laravel Pint (default Laravel preset, no custom config)
|
||||
- **Indentation**: 4 spaces
|
||||
- **Imports**: Fully qualified, one per line, grouped (PHP classes, then Laravel, then app)
|
||||
- **Models**: Use `$fillable` array (not `$guarded`). Use `casts()` method (not `$casts` property). Relationships return typed `HasMany`/`BelongsTo`/etc.
|
||||
- **Controllers**: Return type hints (`Response`, `JsonResponse`, `RedirectResponse`). Use route-model binding. Use `Inertia::render()` for page responses.
|
||||
- **Migrations**: Anonymous class style: `return new class () extends Migration { ... }`
|
||||
- **Error messages**: German. Flash via `->with('success', '...')`. JSON errors use `message` key.
|
||||
- **Null safety**: Use nullsafe operator `?->` and null coalescing `??`
|
||||
- **DB operations**: Prefer Eloquent, fall back to `DB::table()` for bulk upserts in sync code
|
||||
- **SoftDeletes**: Used on `Song` model. Use `whereNull('deleted_at')` in manual queries.
|
||||
- **Imports**: Fully qualified, one per line, alphabetical. App\ first, then Illuminate\, then external.
|
||||
- **Constructors**: Promoted properties with `private readonly`. Empty body: `{}` on same line.
|
||||
```php
|
||||
public function __construct(
|
||||
private readonly SongMatchingService $songMatchingService,
|
||||
) {}
|
||||
```
|
||||
- **Return types**: Always present. Use union types for multiple returns: `Response|JsonResponse`.
|
||||
- **String concat**: No spaces around `.` operator: `'Fehler: '.$e->getMessage()`
|
||||
- **Null safety**: Nullsafe `?->` and null coalescing `??`. Never suppress with `@`.
|
||||
- **Models**: `$fillable` array (never `$guarded`). `casts()` method (never `$casts` property). Typed relationship returns (`HasMany`, `BelongsTo`). `Attribute::get()` for computed accessors.
|
||||
- **Migrations**: Anonymous class: `return new class extends Migration {`. Methods: `up(): void`, `down(): void`.
|
||||
- **Error messages**: German, Du-form. Flash: `->with('success', '...')`. JSON: `'message'` key.
|
||||
- **Validation**: Inline `$request->validate([...])` with `Rule::in()` for enums.
|
||||
- **Transactions**: `DB::transaction(function () use (...): void { ... })` for multi-step writes.
|
||||
- **Constants**: `private const NAME = [...]` for fixed value sets.
|
||||
|
||||
## Code Style — Vue / Frontend
|
||||
## Code Style -- Vue / Frontend
|
||||
|
||||
- **Vue 3 Composition API** only, always `<script setup>`. No Options API.
|
||||
- **Props**: `defineProps({ propName: { type: Type, default: value } })`
|
||||
- **Emits**: `defineEmits(['event-name'])`
|
||||
- **Imports**: Use `@/` alias for `resources/js/`. Vue imports from `'vue'`, Inertia from `'@inertiajs/vue3'`.
|
||||
- **Functions**: Prefer `function name() {}` declarations in components (not `const name = () => {}`)
|
||||
- **Styling**: Tailwind CSS v4 utility classes inline. Scoped `<style>` only when necessary (e.g. drag-and-drop).
|
||||
- **State**: `ref()` for reactive state, `computed()` for derived. Use `watch()` for side effects.
|
||||
- **Routing**: Use `route('name', params)` (Ziggy) for URL generation. Use `router.post/get/delete` from Inertia.
|
||||
- **Testing attributes**: Add `data-testid="..."` on interactive elements for Playwright e2e tests.
|
||||
- **All user-facing text must be German** (Du-form, not Sie).
|
||||
- **Vue 3 Composition API** only. Always `<script setup>`. No Options API.
|
||||
- **Props**: `defineProps({ name: { type: Type, default: value } })`
|
||||
- **Emits**: `defineEmits(['kebab-case-event'])`
|
||||
- **Model**: `defineModel({ type: String, required: true })` for v-model binding.
|
||||
- **Imports**: `@/` alias for `resources/js/`. Vue from `'vue'`, Inertia from `'@inertiajs/vue3'`.
|
||||
- **Functions**: Prefer `function name() {}` declarations (not `const name = () => {}`).
|
||||
- **State**: `ref()` for reactive, `computed()` for derived, `watch()` for side effects.
|
||||
- **Routing**: `route('name', params)` via Ziggy. `router.post/get/delete` from Inertia with `{ preserveScroll: true }`.
|
||||
- **Styling**: Tailwind CSS v4 utility classes inline. `<style scoped>` only when necessary (e.g. drag-and-drop).
|
||||
- **Test IDs**: `data-testid="..."` on interactive elements for Playwright.
|
||||
- **All UI text**: German, Du-form (not Sie).
|
||||
|
||||
## Code Style — Tests
|
||||
## Code Style -- Tests
|
||||
|
||||
- **Framework**: Pest v4 (wraps PHPUnit). Feature tests are class-based extending `TestCase` with `RefreshDatabase`.
|
||||
- **Naming**: `test_snake_case_german_description` (e.g. `test_service_kann_abgeschlossen_werden`)
|
||||
### PHP Tests (Pest v4 + PHPUnit)
|
||||
|
||||
Both styles are used:
|
||||
- **Function-based Pest**: `test('snake_case german description', function () { ... });` with `beforeEach`
|
||||
- **Class-based**: `final class NameTest extends TestCase { use RefreshDatabase; public function test_snake_case(): void {} }`
|
||||
- **Auth**: `$this->actingAs(User::factory()->create())`
|
||||
- **Vite**: Call `$this->withoutVite()` before testing Inertia page renders
|
||||
- **Time**: Use `Carbon::setTestNow('2026-03-01 10:00:00')` for deterministic time
|
||||
- **Assertions**: `assertInertia(fn ($page) => $page->component('...')->has('...')->where('...'))` for Inertia responses
|
||||
- **DB**: Tests use in-memory SQLite (configured in `phpunit.xml`)
|
||||
- **e2e**: Playwright (TypeScript), `tests/e2e/`, baseURL `http://cts-work.test`, auth via `auth.setup.ts`
|
||||
- **Vite**: Call `$this->withoutVite()` before Inertia page render tests
|
||||
- **Time**: `Carbon::setTestNow('2026-03-01 10:00:00')` for deterministic dates
|
||||
- **Inertia assertions**: `$response->assertInertia(fn ($page) => $page->component('...')->has('...')->where('...'))`
|
||||
- **DB**: In-memory SQLite (`phpunit.xml`: `DB_CONNECTION=sqlite`, `DB_DATABASE=:memory:`)
|
||||
- **Helper functions**: Define at file level for shared test setup (e.g. `function makePngUpload(...)`)
|
||||
- **Storage**: `Storage::fake('public')` in `beforeEach` for file upload tests
|
||||
|
||||
## Key Conventions
|
||||
### ProPresenter Parser Tests (PHPUnit 11)
|
||||
|
||||
- **CTS API is READ-ONLY** — never write/modify data via ChurchTools API
|
||||
- **Immediate persistence** — all user actions save instantly, no separate "save" button
|
||||
- **German locale** — `APP_LOCALE=de`, all UI text in German, Du-form
|
||||
- **File uploads** — images convert to JPG 1920x1080 (maintain aspect ratio, no cropping); PPT/PPTX convert to multiple JPGs
|
||||
- **Named routes** — all routes have names, use `route('name')` everywhere
|
||||
- **ProPresenter (.pro) parser** — placeholder only, not yet implemented
|
||||
- Uses `#[Test]` attribute (not `test_` prefix)
|
||||
- `setUp()`/`tearDown()` for temp directory management
|
||||
- Fixtures in `ref/` directory, loaded via `dirname(__DIR__, 2).'/ref/...'`
|
||||
- Strict mode: `failOnRisky`, `failOnWarning`, `beStrictAboutOutputDuringTests`
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
|
||||
- TypeScript in `tests/e2e/`, baseURL `http://cts-work.test`
|
||||
- Auth via `auth.setup.ts` (XSRF token + `/dev-login` endpoint, saves state to `.auth/user.json`)
|
||||
- Selectors: `page.getByTestId('...')`, `page.getByText('...')`, `page.getByRole('...')`
|
||||
- German text assertions: `await expect(page.getByText('Mit ChurchTools anmelden')).toBeVisible()`
|
||||
|
||||
## Key Constraints
|
||||
|
||||
- **CTS API is READ-ONLY** -- never write/modify data via ChurchTools API
|
||||
- **Immediate persistence** -- all user actions save instantly (no separate save button)
|
||||
- **German locale** -- `APP_LOCALE=de`, all UI text German Du-form
|
||||
- **File uploads** -- images convert to JPG 1920x1080 (maintain aspect, no crop); PPT/PPTX to multiple JPGs
|
||||
- **Named routes** -- all routes have names, use `route('name')` everywhere
|
||||
- **No type suppression** -- never use `as any`, `@ts-ignore`, `@ts-expect-error` in frontend
|
||||
- **SoftDeletes** -- used on Song and Slide models; use `whereNull('deleted_at')` in manual queries
|
||||
|
|
|
|||
Loading…
Reference in a new issue