# AGENTS.md

This file provides guidance to agents when working with code in this repository.

## Project Overview

CodeIgniter 4 PHP application for laboratory management (GDC CMOD). Handles specimen tracking, request validation, and result management with role-based access control. SQL Server database with Firebird legacy patient data.

## Tool Usage

Always use Serena MCP tools for anything possible:
- Use `serena_find_symbol` instead of grep when looking for classes, methods, or functions
- Use `serena_search_for_pattern` instead of grep for code pattern searches
- Use `serena_read_file` or `serena_replace_content` instead of Read/Edit tools
- Use `serena_find_referencing_symbols` to find where symbols are used
- Use `serena_replace_symbol_body` or `serena_insert_after_symbol` for code modifications
- Only use Bash for shell commands (git, composer, php, etc.)

## Commands

```bash
# Run all tests
composer test
./vendor/bin/phpunit

# Run single test file
./vendor/bin/phpunit tests/unit/HealthTest.php

# Run single test method
./vendor/bin/phpunit tests/unit/HealthTest.php --filter testIsDefinedAppPath

# Development server
php spark serve

# List all routes
php spark list

# Create controller/model
php spark make:controller Admin
php spark make:model User
```

## PHP Standards

- PHP 8.1+ features (typed properties, match expressions)
- Always declare return types for public methods
- No comments unless explaining complex logic
- Use `esc()` when outputting user data in views

## Naming Conventions

| Type | Convention | Example |
|------|------------|---------|
| Classes | PascalCase | `Admin`, `UserController` |
| Methods/Variables | camelCase | `getUsers()`, `$userId` |
| Constants | UPPER_SNAKE_CASE | `DB_HOST` |
| Database tables | UPPER_SNAKE_CASE | `GDC_CMOD.dbo.USERS` |
| Views | lowercase_underscores | `admin/index.php` |

## Role-Based Access Control

| Role ID | Name | Route Prefix |
|---------|------|--------------|
| 0 | Superuser | `/superuser` |
| 1 | Admin | `/admin` |
| 2 | Lab | `/lab` |
| 3 | Phlebo | `/phlebo` |
| 4 | CS | `/cs` |

```php
// Single role
['filter' => 'role:1']
// Multiple roles
['filter' => 'role:1,2']
```

## Controller Patterns

```php
namespace App\Controllers;

class Admin extends BaseController {
    public function index() { }
}

// API Controllers use ResponseTrait
use App\Controllers\BaseController;
use CodeIgniter\API\ResponseTrait;

class Users extends BaseController {
    use ResponseTrait;
    protected $db;

    public function __construct() {
        $this->db = \Config\Database::connect();
        helper(['url', 'form', 'text']);
    }
}
```

## Database Operations

```php
$this->db = \Config\Database::connect();

// Parameterized queries only
$query = $this->db->query("SELECT * FROM table WHERE id = ?", [$id]);
$row = $query->getRowArray();
$results = $query->getResultArray();

// Transactions
$this->db->transBegin();
try {
    $this->db->query("INSERT INTO ...", [$data]);
    $this->db->transCommit();
} catch (\Throwable $e) {
    $this->db->transRollback();
}
```

## Request/Response Patterns

```php
// GET input
$date1 = $this->request->getVar('date1') ?? date('Y-m-d');

// POST JSON
$input = $this->request->getJSON(true);

// JSON response
return $this->respond(['data' => $results]);
return $this->response->setJSON(['message' => 'Success']);

// View response
return view('admin/index', $data);

// Redirect with errors
return redirect()->back()->with('errors', ['key' => 'message']);
```

## Session Structure

```php
session()->set([
    'isLoggedIn' => true,
    'userid' => (string) $user['USERID'],
    'userroleid' => (int) $user['USERROLEID'],
    'userrole' => (string) $role,
]);
```

## Validation Endpoints

- `POST /api/{resource}/validate/{id}` - validate a record
- `DELETE /api/{resource}/validate/{id}` - unvalidate a record

## Security

- Use parameterized queries (never interpolate directly)
- Hash passwords with `password_hash()` / `password_verify()`
- Validate and sanitize all input before use

## Database Schema

- Primary: SQL Server (`GDC_CMOD.dbo`)
- Legacy: Firebird (`GLENEAGLES` via ODBC)
- No CI4 Models - raw SQL queries via `Database::connect()`

## Dual-Level Validation

Validation requires 2 different users:
1. First: `ISVAL1=1`, `VAL1USER`, `VAL1DATE`
2. Second (different user): `ISVAL2=1`, `VAL2USER`, `VAL2DATE`
