# CLAUDE.md — CoreCore Multi-tenant admin application built on MintyPHP. Manages tenants, users, departments, roles, permissions, address books, mail, CSV imports, scheduled jobs, and Microsoft Entra ID SSO. > **Agent workflow:** guards, quality gates, role prompts, contracts, and skills live in `.agents/` — start with `.agents/README.md`. ## Tech Stack - **Runtime:** PHP 8.5 (FPM) on MintyPHP framework (`mintyphp/core`) - **Web server:** Nginx 1.25 (Alpine) - **Database:** MariaDB 11.4 - **Cache:** Memcached 1.6 - **Frontend:** Vanilla JS (ES2022 modules), vanilla CSS with `@layer` system — no build step - **Key PHP libs:** PHPMailer 7, Dompdf 3, endroid/qr-code 6, league/commonmark 2 - **Containerized:** Docker Compose (dev + prod variants) ## Quick Commands ```bash # Start dev environment docker compose up --build -d # App: http://localhost:8080 | phpMyAdmin: http://localhost:8081 # Run PHPUnit tests (phpunit.xml configures bootstrap + testsuite) docker compose exec php vendor/bin/phpunit # Run PHPStan (level 5) docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress # CLI commands (single entry point) docker compose exec php php bin/console list docker compose exec php php bin/console module:sync # full module runtime sync docker compose exec php php bin/console module:migrate # apply module SQL migrations docker compose exec php php bin/console module:permissions-sync # sync + deactivate orphaned docker compose exec php php bin/console module:build # build runtime page root docker compose exec php php bin/console module:assets-sync # publish module web assets docker compose exec php php bin/console module:deactivate --confirm docker compose exec php php bin/console scheduler:run docker compose exec php php bin/console doctor # Check unused Composer packages docker compose exec php vendor/bin/composer-unused ``` ## Project Structure ``` lib/ # All backend PHP (namespace MintyPHP\) App/ # DI container + bootstrap (AppContainer, Container/Registrars/) Module/ # Module platform (Registry, Manifest, Autoloader, Builder) Repository/ # SQL queries, prepared statements, filters/paging Service/ # Business logic, validation, orchestration Http/ # Auth guards, API auth, request helpers Input/ # Request input handling (RequestInput, FormErrors) Console/ # CLI command framework (ConsoleApplication, Command base class) Commands/ # Command classes (Module/, Scheduler/, Tools/) Support/ # Crypto, flash, guard, search, helpers modules/ # Self-contained feature modules (namespace MintyPHP\Module\\) /module.php # Module manifest (routes, slots, providers, permissions) /lib/ # Module PHP classes (Service, Repository, Providers) /pages/ # Module actions + views (symlinked into runtime) /templates/ # Module-specific templates /web/ # Module CSS/JS (published to web/modules//) /tests/ # Module PHPUnit tests pages/ # Core actions (controllers) — file-based routing pages/**/*.php # Action files (read input, check permissions, call services) pages/**/*.phtml # View files (rendering only, no DB calls) templates/ # Core layouts and partials partials/ # Reusable UI components emails/ # Email HTML templates pdfs/ # PDF templates (Dompdf) config/ # App config (routes, assets, settings cache, themes, modules) web/ # Document root (entry point, CSS, JS, static assets) js/core/ # DOM utilities, Grid.js factory, component runtime js/components/ # Reusable UI components css/ # Layered CSS (base → components → layout → pages) modules/ # Published module assets (symlinks to modules//web/) storage/ # Uploaded files + runtime state runtime/pages/ # Symlinked page tree (core + modules, built by module-build) db/init/init.sql # Full schema + seed data (no migration framework) db/updates/ # Idempotent SQL update scripts for existing installs tests/ # PHPUnit tests (namespace MintyPHP\Tests\) docs/ # German-language documentation (Markdown) i18n/ # Translation files (de, en) bin/ # CLI entry point (bin/console) + legacy scripts + shell tools docker/ # Dockerfiles, Nginx configs, PHP configs .agents/ # Agent workflow system (contracts, prompts, checks, skills, runs) ``` ## Architecture Rules ### Strict Layering (enforce in all changes) 1. **App** (`lib/App/`): DI container bootstrap. Registers all service/repository factories. No business logic. 2. **Repository** (`lib/Repository/`): All SQL lives here. No HTTP, session, or rendering. 3. **Service** (`lib/Service/`): Business rules and validation. No HTML. Four internal sub-types: - **Service** (`*Service.php`): Business logic + orchestration. Coordinates repositories and gateways. - **Gateway** (`*Gateway.php`): Adapter for a single external dependency (repository, settings, HTTP API, scope). No orchestration flow. - **Factory** (`*Factory.php`): Only place inside `lib/Service/` that may call `new` on services/gateways/repositories. Registers via DI container. - **Policy** (`*Policy.php` in `lib/Service/Access/`): Authorization decisions per resource type. Returns `AuthorizationDecision`. No HTTP, no business logic. 4. **Action** (`pages/**/*.php`): Reads input, checks permissions + tenant scope, calls services, sets view vars. 5. **View** (`pages/**/*.phtml`): Pure rendering. **No DB calls.** HTML escaping via `e(...)`. 6. **Template** (`templates/`): Layouts and partials. ### Modules - Modules are self-contained feature packages under `modules//` - **Namespace:** `MintyPHP\Module\\*` — never use core namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`) - **Manifest:** `module.php` declares routes, UI slots, providers, permissions, scheduler jobs - **UI integration:** Only via platform slots (`aside.tab_panel`, `topbar.right_item`, `layout.head_style`, `runtime.component`, etc.) — no core template hardcoding - **Session keys:** Prefixed with `module..*` - **Activation:** `config/modules.php` or `APP_ENABLED_MODULES` env override - **Runtime sync:** `php bin/module-runtime-sync.php` after any module/manifest change (builds page symlinks, syncs permissions, publishes assets) - **Fingerprint guard:** `web/index.php` validates runtime state matches active modules; fails fast if stale - **API isolation:** API requests (`api/` prefix) skip session/auth/cookie flows entirely; modules using API endpoints rely on `ApiBootstrap.php` ### Routing - File-based: `pages/admin/users/edit($id).php` → URL parameter `id` - Module pages: `modules//pages/` symlinked into `storage/runtime/pages/` at build time - `...(none).phtml` → no template layout - `data().php` suffix → data/AJAX endpoints - Named aliases in `config/routes.php` with `public` flag for auth guard - Module routes declared in manifest, merged at boot (fail-fast on collision) ### PHP Conventions - PSR-4 autoload: `MintyPHP\` → `lib/`, `MintyPHP\Module\*` → `modules/*/lib/`, `MintyPHP\Tests\` → `tests/` - All user-facing text uses `t('key')` translation helper - Permissions + tenant scope enforced in actions/services, never just in UI - Security/integration settings stay in DB, not in `config/settings.php` file cache ### Frontend Conventions - Vanilla ES2022 modules, no bundler - CSS classes prefixed with `app-` - Defensive DOM checks (elements may not exist on every page) - No business logic in frontend - Assets served raw with `assetVersion()` cache-busting (filemtime-based) ### UI Patterns - Edit pages: tabs → `Master data` | `Visibility` | optional `Danger zone` - Titlebar partial for primary actions (Save, Save & close) - Secondary actions in Aside block via `app-details-aside-actions.phtml` - Lists use Grid.js - All visible text wrapped in `t('...')` ## Database - Schema defined in `db/init/init.sql` (applied once on container init) - No migration framework — schema changes for existing installs are idempotent SQL scripts - Settings stored in `settings` DB table (single source of truth); `config/settings.php` is a partial file cache for hot-path UI reads only ## Quality Gates - **PHPStan level 5** — scans `config/`, `lib/`, `modules/`, `pages/`, `tests/` - **PHPUnit 11** — bootstrap: `tests/bootstrap.php` - **Frontend Smoke-Check** — browser console bleibt fehlerfrei in Kernflows - **composer-unused** — periodic check for unused dependencies ## Environment - Config via `.env` (gitignored) — copy from `.env.example` for dev, `.env.prod.example` for prod - `config/config.php` (gitignored) — copy from `config/config.php.example` - `APP_CRYPTO_KEY` (64-char hex) needed for AES-256-GCM encryption of SSO secrets