# 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. ### PrintStream background import `scripts/import-printstream.php` runs every 30 minutes via cron inside the Docker container. It: 1. Reads all boards with `import_from_printstream = 1` 2. Parses `printstream_job_name` into filter tokens (newline-separated) 3. Connects to the PrintStream SQL Server (`KCI-PS-2024 / Livedata_dosrun`) via **FreeTDS + pdo_odbc** 4. For each token runs a CTE query against `dbo.SCHEDFIL`, `dbo.ESTIMATE`, `dbo.DEBTOR`, `dbo.NOTES` 5. Inserts new cards (first column, first lane) or refreshes PrintStream fields on existing cards **Schedule:** `docker/crontab` — `*/30 * * * *`. To change the interval, update both the crontab line and `IMPORT_RUN_EVERY_MINUTES` in `.env`. **Log:** `/var/log/kanban-import.log` inside the container (`docker exec tail -f /var/log/kanban-import.log`). **SQL Server connection:** FreeTDS via ODBC. DSN built from `PRINTSTREAM_*` env vars in `.env`. No Microsoft drivers required. TDS version 7.4 (SQL Server 2012+). **Dockerfile dependencies added for this feature:** `freetds-dev freetds-bin unixodbc-dev tdsodbc cron` (apt) + `pdo_odbc` (PHP ext). Do not remove them. 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: ... ```