chore(github): add composer to dependabot, fix stale AI instructions

dependabot.yml: the comment claimed "no Composer/npm manifests committed",
but src/composer.json + composer.lock ARE committed. Added a grouped
composer ecosystem (directory /src) so the 4 prod deps and the dev tools
get advisory monitoring; noted the production-only vendor recommit step.

instructions/php-conventions + architecture-rules: rewritten to the
current Composer PSR-4 architecture. They previously described the
pre-migration state and misdirected Copilot:
- "No autoloading via Composer — custom src/autoload.php" (file removed)
  and "Do NOT introduce Composer dependencies" → Composer PSR-4, vendor
  committed production-only, dump-autoload workflow
- lowercase paths (src/cli, src/core, src/modules, …) → real PascalCase
  (src/Cli, src/Core, src/Modules, src/Streaming, …)
- setDb() / $r-prefix (both gone from the codebase) → DatabaseAware +
  self::db(); dropped the dead $r naming rule
- inverted namespace guidance → new code is namespaced; legacy coexists,
  don't mass-migrate
- dangling ARCHITECTURE.md ref → docs/en/development/architecture.md
  (which exists), src/config/modules.php path fixed
This commit is contained in:
Divarion_D
2026-08-07 20:11:26 +03:00
parent ef521b40a0
commit 2abd19b0d5
3 changed files with 109 additions and 97 deletions
@@ -1,55 +1,64 @@
---
description: "Use when creating or modifying services, repositories, controllers, modules, or any domain/core code. Enforces XC_VM architectural rules from ARCHITECTURE.md."
description: "Use when creating or modifying services, repositories, controllers, modules, or any domain/core code. Enforces XC_VM architectural rules — see docs/en/development/architecture.md."
---
# Architecture Rules — XC_VM
## Layer Pattern
Every domain context follows: **Controller → Service → Repository → Database**
Structured PHP monolith with a modular extension layer — intentionally **no DDD /
Hexagonal / Clean Architecture**. Split by context with minimal abstractions.
Authoritative reference: `docs/en/development/architecture.md`.
## Source tree (PSR-4, `XcVm\` → `src/`)
| Path | Role |
|------|------|
| `src/Core/` | Infrastructure primitives: DI container, events, HTTP router, config, auth, logging |
| `src/Domain/` | Business contexts: Stream, VOD, Line, User, Server, Security, … (`Controller → Service → Repository`) |
| `src/Infrastructure/` | Cross-cutting adapters (Database, TMDb client, …) |
| `src/Streaming/` | Streaming runtime — used by both MAIN and LB builds |
| `src/Public/` | Front controller, router, controllers, views, assets |
| `src/Cli/` | Console commands and cron entry points |
| `src/Modules/` | Optional extension layer (`{name}_{hash5}/`), loaded by `ModuleLoader` |
| `src/ministra/` | Stalker Portal — isolated subsystem (`BoundaryInterface`) |
## Layer pattern
Every domain context follows **Controller → Service → Repository → Database**:
```
public/Controllers/Admin/StreamController.php → presentation
domain/Stream/StreamService.php → business logic
domain/Stream/StreamRepository.php → data access
core/Database/Database.php → infrastructure
Public/Controllers/Admin/StreamController.php → presentation
Domain/Stream/StreamService.php → business logic
Domain/Stream/StreamRepository.php → data access
Infrastructure/Database/… + Core/Database/… → infrastructure
```
## Dependency Direction (strict)
## Dependency direction (inward only)
| Layer | Can depend on | CANNOT depend on |
|-------|---------------|------------------|
| `public/` | `domain/` (Service+Repository), `core/` | `streaming/`, `modules/` directly |
| `domain/` | `core/` (Database, Cache, Events) | `public/`, `streaming/`, `modules/`, `infrastructure/` |
| `core/` | Only other `core/` subdirectories | Everything else |
| `streaming/` | `core/` (subset), `domain/` (read-only) | `public/`, `modules/` |
| `modules/` | `domain/`, `core/` | Other modules, `public/`, `streaming/` |
| Layer | May depend on | MUST NOT depend on |
|-------|---------------|--------------------|
| `Public/` | `Domain/` (Service + Repository), `Core/` | `Streaming/`, `Modules/` directly |
| `Domain/` | `Core/`, `Infrastructure/` | `Public/`, `Modules/` |
| `Streaming/` | `Core/` (subset), `Domain/` (read-only) | `Public/`, `Modules/` |
| `Core/` | Only other `Core/` subdirectories | Everything else |
| `Modules/` | `Domain/`, `Core/` | Other modules, `Public/`, `Streaming/` |
## Constructor Injection
- `ServiceContainer` is used ONLY in bootstrap (composition root)
- After bootstrap, all dependencies are passed via constructor
- No service may call `$container->get()` inside its methods
- Legacy exceptions marked with `// @legacy-container`
## Dependency injection
- `ServiceContainer` (PSR-11) is used ONLY at the composition root (bootstrap paths / module `boot()`).
- After bootstrap, collaborators are passed by constructor; the shared DB connection comes via the `DatabaseAware` trait + `self::db()`.
- No service calls `$container->get()` inside its own methods (no Service Locator).
## File Consolidation Rules
- Do NOT create separate file if class has < 150 lines AND < 5 public methods
- Small Repository + Service in same context → combine in one file
- Service with 1-2 methods → merge into nearest related context
- Separate files only when class > 150 lines OR ≥ 5 public methods OR used from 3+ contexts
## Module boundaries
- A module is an isolated directory `src/Modules/{name}_{hash5}/` (`{Pascal}Module extends BaseModule`, namespace `XcVm\Module\{Pascal}`).
- **Modules own their DB schema** via file migrations (`migrations/<semver>.sql` + `database.sql` master + `database_drop.sql` teardown, run by `ModuleMigrator`). Do NOT add module tables to core `src/bin/install/database.sql`.
- **Core must never touch module-owned tables directly** (a module can be uninstalled, dropping them). Instead core dispatches an event (e.g. `StreamsDeletedEvent`) and the owning module subscribes via `#[ListensTo(Event::class)]` to clean up.
- Removing/disabling a module (via `src/config/modules.php`) must NOT break the system — graceful degradation.
## Module Boundaries
- Module = isolated directory under `src/modules/`
- Removing a module must NOT break the system (graceful degradation)
- Modules depend on `domain/` and `core/`, never on other modules
- Core never depends on modules
## Multi-build awareness
- **MAIN build:** full panel (admin + streaming + MySQL/MariaDB).
- **LoadBalancer build:** streaming subset only — admin/reseller/player and privileged CLI are stripped (`make lb`). Modules are MAIN-only. Code in `Domain/` / `Streaming/` reached by the LB must not pull admin-only dependencies.
## Decision Filters (from ARCHITECTURE.md)
Before any architectural decision, apply:
1. Can a contributor understand it in 5 minutes? → if no, simplify
2. Does it break the streaming hot path? → if yes, reject
3. Can it be isolated as a module? → if no, justify why
## Decision filters
Before an architectural decision, apply:
1. Can a contributor understand it in 5 minutes? If no → simplify.
2. Does it break the streaming hot path? If yes → reject.
3. Can it be isolated as a module? If no → justify why.
If a decision improves "code beauty" but raises the entry barrier → reject it.
## Multi-Build Awareness
- **MAIN build**: full admin + streaming
- **LoadBalancer build**: streaming subset only, admin dirs stripped
- Code in `domain/` used by streaming must NOT pull admin-only dependencies
If a change improves "code beauty" but raises the entry barrier → reject it.
@@ -3,67 +3,51 @@ applyTo: "**/*.php"
---
# PHP Conventions — XC_VM
XC_VM is a PHP 8.1+ modular monolith **migrated to Composer PSR-4** (`XcVm\` → `src/`).
The application root is `src/` (composer.json, vendor/, bootstrap.php all live there).
`short_open_tag=1` is on — view templates use `<?` / `<?=`.
## Formatting
- **Brace style:** K&R (opening brace on same line)
- **Indentation:** Tabs (1 tab per level), NOT spaces
- **Brace style:** K&R (opening brace on the same line)
- **Indentation:** tabs (1 tab per level), NOT spaces
- **No trailing whitespace**
## Comment Language
- **New code:** write all comments and DocBlocks in **English** (keep PHPDoc tags
`@param`, `@return`, `@package`, etc. in English as they already are).
- **Legacy files:** the existing codebase has Russian-language DocBlocks in ~23%
of files (notably `src/cli/`, `src/core/Auth/`, `src/public/Controllers/`).
Do NOT mass-translate. Translate Russian comments to English only when you are
already editing that file for another reason, or when explicitly asked.
- **Do not mix languages** within a single new comment block.
## Autoloading & dependencies
- **Composer PSR-4:** `XcVm\` maps to `src/` (e.g. `XcVm\Core\Database\DatabaseHandler` = `src/Core/Database/DatabaseHandler.php`). Modules load via their own namespace autoloader, not the Composer PSR-4 map.
- **`vendor/` is committed and PRODUCTION-ONLY.** Never run `composer install` on a deploy path. To change the autoload map, run `composer dump-autoload` from `src/`. After changing deps, re-commit a `composer install --no-dev` vendor and `composer.lock`.
- **Don't add dependencies casually** — a new Composer package means a lockfile update and a production-only vendor recommit. Prefer the standard library or existing vendored libs.
## Typing and Declarations
- **`declare(strict_types=1)`:** legacy code (controllers, `cli/`, most of `core/`) omits it — do NOT add it there. New code already uses it (e.g. `src/core/Auth/*`, `src/core/Enum/*`, `src/core/Events/ListensTo.php`) — follow the pattern of neighbouring files.
- **Namespaces:** legacy first-party code has none — do NOT add namespaces to it. The module system *is* namespaced (`src/modules/*/`…`Module.php`), as are bundled parsers (`core/Parsing/M3uParser`, `core/Parsing/PhpM3u8`) — match the surrounding file when working there.
- Do NOT add PHP docblocks or type annotations to existing code unless explicitly asked
- Parameter type hints: use when writing new service/repository methods, omit when editing legacy code
## Namespaces & strict types (new code)
- **New PHP files** declare a namespace under `XcVm\…` matching their path, and start with `declare(strict_types=1)`.
- **Legacy first-party files** that are not yet namespaced (roughly half the tree) still exist. Do NOT mass-migrate them: only add a namespace / `strict_types` when you are already rewriting that file for another reason. Match the surrounding file when editing.
- Modules are namespaced `XcVm\Module\{Pascal}` with the class `{Pascal}Module` in `src/Modules/{name}_{hash5}/`.
## Database access
- **New code** uses the `DatabaseAware` trait: `use \XcVm\Infrastructure\Database\DatabaseAware;` then call `self::db()` (it resolves the connection from the `DatabaseFactory` singleton). Do NOT reintroduce per-class `setDb()` wiring.
- `Core/Database/DatabaseHandler` is the PDO wrapper. Query with `?` placeholders, never named parameters: `self::db()->query('SELECT * FROM streams WHERE id = ?;', $id)`. Terminate the SQL string with a semicolon.
- Legacy superglobals (`$db`, `$rSettings`, `$rUserInfo`, `$rServers`) are still bridged by `LegacyInitializer` for legacy code — preserve them when editing legacy files, but never introduce new global state in new code.
## Naming
- **Classes:** PascalCase (`StreamService`, `Database`, `FileLogger`)
- **Methods:** camelCase (`getById`, `processStream`, `closeConnection`)
- **Variables:** `$r` prefix for data arrays and results: `$rData`, `$rArray`, `$rReturn`, `$rRow`, `$rStreamID`
- **Constants:** UPPER_SNAKE_CASE (`STATUS_SUCCESS`, `GIT_OWNER`); for typed sets prefer enums (`BootContext`, `ModuleState`) over new string constants
- **Database columns in queries:** snake_case as stored in DB
- **Classes:** PascalCase (`StreamService`, `DatabaseHandler`)
- **Methods:** camelCase (`getById`, `processStream`)
- **Constants:** UPPER_SNAKE_CASE; for typed sets prefer enums (`BootContext`, `ModuleState`) over new string constants
- **DB columns in queries:** snake_case as stored
## Class Patterns
- Services use `public static` methods in current codebase (legacy pattern)
- New domain code follows Controller → Service → Repository pattern per `docs/en/development/architecture.md`
- Constructor injection for new services; legacy code still uses `global $db`, `global $rSettings`
## Class patterns
- New domain code follows **Controller → Service → Repository** (see `docs/en/development/architecture.md`).
- Prefer constructor injection for collaborators; use the `DatabaseAware` trait for the shared connection.
## Global Variables (legacy — do NOT introduce new ones)
- `$db` — Database instance
- `$rSettings` — System settings array
- `$rUserInfo` — Current user info
- `$rServers` — Servers configuration
- `$_TITLE` — Page metadata
## Comments
- **New code:** English comments and DocBlocks (keep `@param`, `@return`, `@package` tags in English).
- **Legacy files** carry Russian-language DocBlocks in some files. Do NOT mass-translate; translate only a file you are already editing for another reason, or when asked. Don't mix languages within one comment block.
When editing legacy files that use globals, preserve the pattern. Do NOT refactor globals unless explicitly asked.
## Error handling
- Prefer typed exceptions from the `XcVmException` hierarchy for new code.
- For fatal API responses the codebase uses `exit(json_encode(...))` — match the surrounding handler.
- Do NOT wrap existing code in try-catch "just in case".
## SQL
- Use PDO prepared statements with `?` placeholders: `$db->query('SELECT * FROM streams WHERE id = ?;', $rID)`
- Do NOT use named parameters (`:name` style)
- Terminate SQL strings with semicolon inside the query string
## Error Handling
- Use try-catch + `exit(json_encode(...))` for fatal API errors
- Use status constants (`STATUS_SUCCESS`, etc.) for validation flows
- Do NOT add exception handling to code that doesn't have it unless asked
## File Structure
- No autoloading via Composer — project uses custom `src/autoload.php`
- Admin pages: `src/public/Views/admin/*.php` — mixed PHP/HTML templates (legacy-compatible)
- Domain services: `src/domain/{Context}/{ContextService}.php`
- Core utilities: `src/core/{Subsystem}/*.php`
## What NOT to Do
- Do NOT add `use` / `namespace` to legacy first-party code (controllers, `cli/`, legacy `core/`) — but keep namespaces when editing the module system (`src/modules/*`), which is already namespaced
- Do NOT introduce Composer dependencies
- Do NOT restructure legacy files into PSR-4 layout (modules are already namespaced / PSR-4-like)
- Do NOT convert existing `global` usage to DI without explicit request
- Do NOT add PHPDoc to unchanged code
- Do NOT wrap existing code in try-catch "just in case"
## What NOT to do
- Do NOT run `composer install` on a deploy path, and do NOT add Composer dependencies casually.
- Do NOT reintroduce `setDb()` static injection or new `global` usage in new code.
- Do NOT mass-migrate legacy files to namespaces / `strict_types` / DI without an explicit request — keep changes surgical.
- Do NOT add PHPDoc or type annotations to unchanged code.