# AGENTS.md ## Purpose Describe how AI coding agents should work in this repository. This file is intentionally small. It acts as the repository-level router and startup guide. Detailed rules live in focused files under `./.ai/skills/` and should be loaded only when relevant to the current task. --- ## Startup Instructions Before working on any task: 1. Read this `AGENTS.md`. 2. Read the main skill index: ```text ./.ai/SKILLS.md ``` 3. Load only the relevant skill files for the current task. 4. Follow project-specific instructions before general best practices. 5. Inspect existing code before introducing new patterns. 6. State important assumptions before making major changes. 7. Prefer small, focused, reviewable changes. --- ## Instruction Priority When instructions conflict, follow this order: 1. User's current request 2. Safety, security, privacy, and data-loss prevention 3. This `AGENTS.md` 4. `./.ai/SKILLS.md` 5. Referenced files in `./.ai/skills/` 6. Existing project conventions 7. General best practices --- ## Project Summary This project is the **KCI Kanban Board** — a print/mail job tracking kanban application built on the **MindVisionCode PHP** framework. It was migrated from an ASP Classic implementation (source: `https://onefortheroadgit.sytes.net/dcovington/KCI-KANBAN`). ### What this app does - Manage multiple kanban **boards** (each board has a slug-based URL) - Each board has configurable **columns** (job stages) and **swim lanes** (job categories/priority rows) - **Cards** represent print jobs: job_number, job_name, customer_name, delivery_date, quantity, notes, full_note (PrintStream raw data) - Cards drag-and-drop between cells (column × lane intersection) via SortableJS - Board settings panel: add/rename/delete/reorder columns and swim lanes - **PrintStream integration flag** on boards (`import_from_printstream`, `printstream_job_name`) — the actual import script is a separate process not yet ported to PHP - Authentication via **Keycloak SSO** (OIDC authorization-code flow) ### Domain tables | Table | Purpose | |-------|---------| | `boards` | One row per kanban board | | `board_columns` | Columns (stages) belonging to a board | | `swim_lanes` | Swim lanes (rows) belonging to a board | | `cards` | Job cards placed in a column × lane cell | ### Auth pattern - `app/Services/AuthService.php` — static helpers: `requireLogin()`, `isLoggedIn()`, `getCurrentUsername()` - Page controllers call `if ($guard = AuthService::requireLogin()) return $guard;` - JSON API controllers call `if (!AuthService::isLoggedIn()) return $this->json([...], 401);` - Keycloak config lives in `config/auth.php`, reads from env vars — see `.env.example` ### Repository instantiation pattern Repositories are instantiated directly inside controllers using the `database()` helper (not DI container): ```php private function boards(): BoardRepository { return new BoardRepository(database()); } ``` ### Route ordering rule `/columns/reorder` and `/swimlanes/reorder` **must be registered before** `/columns/{id}` and `/swimlanes/{id}`. If the literal route comes after the param route, "reorder" is treated as an id. See `routes/web.php` comments. ### JSON body endpoints `ColumnsController::reorder()` and `SwimLanesController::reorder()` receive a JSON array body (not form data). They read it with `json_decode(file_get_contents('php://input'), true)`. Do not try to use `$request->input()` for these. ### Board show view `BoardsController::show()` uses `$this->fragment()` (not `$this->view()`) because the kanban board is a fully self-contained HTML page with its own `//` — it does not use the shared `app.php` layout. ### Static assets - `public/css/kanban.css` — kanban grid layout and card styles (copied from ASP repo) - `public/js/kanban-board.js` — grid rendering, drag-drop, search - `public/js/kanban-modal.js` — card create/edit modal - `public/js/kanban-settings.js` — settings panel (add/rename/delete/reorder columns and lanes) The JS posts to `/cards/*`, `/columns/*`, `/swimlanes/*` — the PHP routes must match exactly. ### Composer Run with the PHP binary found at: ``` C:\Users\danielc.NTP\AppData\Local\Microsoft\WinGet\Packages\PHP.PHP.8.5_Microsoft.Winget.Source_8wekyb3d8bbwe\php.exe ``` Composer.phar is at `D:\Development\PHP\PHP-TERRITORY\composer.phar`. Requires `-d extension=php_openssl.dll`. ### Docker build notes The `vendor/` directory is excluded by `.dockerignore`, so `composer install` runs inside the container at build time. The Dockerfile must install `libzip-dev unzip` (apt) and `zip` (PHP ext) **before** the `composer install` step — without them, Composer cannot extract downloaded package archives and exits with code 1. This is already in the Dockerfile. Do not remove those packages if updating the Dockerfile. It is intentionally inspired by a Classic ASP MVC framework style: - Central dispatcher - Controllers and actions - ViewModels - Repository classes - Simple validation - Database migrations - Small, readable files - Minimal dependencies Do **not** turn this project into Laravel, Symfony, Slim, CakePHP, or another large framework. The goal is to keep the framework understandable, practical, and easy to extend. --- ## Main Route Always start here: ```text ./.ai/SKILLS.md ``` Then load only the skill files needed for the task. --- ## Common Skill Routes ```text PHP language, style, Composer, OOP: ./.ai/skills/php/SKILL.md MVC framework architecture, routing, controllers, views, ViewModels: ./.ai/skills/mvc/SKILL.md PDO, repositories, migrations, SQL, database safety: ./.ai/skills/database/SKILL.md Input validation, escaping, passwords, sessions, secrets, web security: ./.ai/skills/security/SKILL.md Tests, static analysis, quality gates, composer scripts: ./.ai/skills/testing/SKILL.md Agent behavior, PR checklist, legacy policy, response format: ./.ai/skills/workflow/SKILL.md ``` --- ## Response Format For non-trivial tasks, respond using this structure: ```text Goal: Route: Assumptions: Plan: Implementation: Tests: Risks: ``` For simple questions, answer directly. --- ## Framework Change Policy The framework core may be modified to add functionality or optimize existing code, but **never silently**. Any time an agent identifies a change to framework-level code (dispatcher, routing, base controller, base repository, migration runner, validation engine, autoloader, or any file under `core/`), it must stop and present the following proposal to the user before writing a single line: ```text FRAMEWORK CHANGE PROPOSAL ========================== Issue: What problem or limitation was found, and where in the framework it lives. Proposed Change: What would be added, modified, or removed. Why It Is Needed: The specific reason application code cannot solve this without a framework change. Risks / Dangers: - Breaking changes to existing controllers, repositories, or views - Behavioral differences across PHP versions - Security surface changes - Performance regressions - Any other relevant concerns Benefits: - What improves or is unlocked by the change Alternatives Considered: Any application-level workarounds that were ruled out and why. Ai Agent Skills Update: - What skills need to be changed to support this framework-level change? Awaiting your approval before proceeding. Reply YES to apply, NO to skip, or ask questions. ``` **Rules:** - Do not apply the change until the user explicitly approves. - If the user says NO, document the limitation as a comment or note and continue with the best available application-level workaround. - Keep framework changes small and focused — one concern per change. - After approval, note the change in the commit message so the history is clear. - Update the proper skill file so that the new process can be applied to all future changes in this repository. --- ## Skill Feedback Rule If project guidance is missing or unclear, suggest an update. ```text Suggested SKILLS.md update: - Add/update: ... - Reason: ... ```