8.8 KiB
8.8 KiB
AGENTS.md - Code Guidelines for CLQMS
CLQMS (Clinical Laboratory Quality Management System) – headless REST API backend built on CodeIgniter 4 with a focus on laboratory workflows, JWT authentication, and synchronized OpenAPI documentation.
Repository Snapshot
app/holds controllers, models, filters, and traits wired through PSR-4App\namespace.tests/relies on CodeIgniter's testing helpers plus Faker for deterministic fixtures.- Shared response helpers and ValueSet lookups live under
app/Librariesandapp/Traitsand should be reused before introducing new helpers. - Environment values, secrets, and database credentials live in
.envbut are never committed; treat the file as a reference for defaults.
Build, Lint & Test
All commands run from the repository root.
# Run the entire PHPUnit suite
./vendor/bin/phpunit
# Target a single test file (fast verification)
./vendor/bin/phpunit tests/feature/Patients/PatientCreateTest.php
# Run one test case by method
./vendor/bin/phpunit --filter testCreatePatientSuccess tests/feature/Patients/PatientCreateTest.php
# Generate scaffolding (model, controller, migration)
php spark make:model <Name>
php spark make:controller <Name>
php spark make:migration <name>
# Database migrations
php spark migrate
php spark migrate:rollback
# After OpenAPI edits
node public/bundle-api-docs.js
Use php spark test --filter <Class>::<method> when filtering more than one test file is cumbersome.
Agent Rules Scan
- No
.cursor/rules/*or.cursorrulesdirectory detected; continue without Cursor-specific constraints. - No
.github/copilot-instructions.mdpresent; Copilot behaviors revert to general GitHub defaults.
Coding Standards
Language & Formatting
- PHP 8.1+ is the baseline; enable
declare(strict_types=1)at the top of new files when practical. - Follow PSR-12 for spacing, line length (~120), and brace placement; prefer 4 spaces and avoid tabs.
- Use short arrays
[], and wrap multiline arguments/arrays with one-per-line items. - Favor expression statements that return early (guard clauses) and keep nested logic shallow.
- Keep methods under ~40 lines when possible; extract private helpers for repeated flows.
Naming & Types
- Classes, controllers, libraries, and traits: PascalCase (e.g.,
PatientImportController). - Methods, services, traits: camelCase (
fetchActivePatients). - Properties: camelCase for new code; legacy snake_case may persist but avoid new snake_case unless mirroring legacy columns.
- Constants: UPPER_SNAKE_CASE.
- DTOs/array shapes: Use descriptive names (
$patientInput,$validatedPayload). - Type hints required for method arguments/returns; use union/nullables (e.g.,
?string) instead of doc-only comments. - Prefer PHPDoc only when type inference fails (complex union or array shapes) but still keep method summaries concise.
Imports & Structure
- Namespace declarations at the very top followed by grouped
usestatements. - Import order: Core framework (
CodeIgniter), thenApp\, then third-party packages (Firebase, Faker, etc.). Keep each group alphabetical. - No inline
usestatements inside methods. - Keep
usestatements de-duplicated; rely on IDE orphpcbfto reorder.
Controller Structure
- Controllers orchestrate request validation, delegates to services/models, and return
ResponseTraitresponses; avoid direct DB queries here. - Inject models/services via constructor when they are reused. When instantiating on the fly, reference FQCN (
new \App\Models\...). - Map HTTP verbs to semantic methods (
index,show,create,update,delete). Keep action methods under 30 lines by delegating heavy lifting to models or libraries. - Always respond through
$this->respond()or$this->respondCreated()so JSON structure stays consistent.
Response & Error Handling
- All responses follow
{ status, message, data }.statusvalues:success,failed, orerror. - Use
$this->respondCreated(),$this->respondNoContent(), or$this->respond()with explicit HTTP codes. - Wrap JWT/external calls in try/catch. Log unexpected exceptions with
log_message('error', $e->getMessage())before responding with a sanitized failure. - For validation failures, return HTTP 400 with detailed message; unauthorized access returns 401. Maintain parity with existing tests.
Database & Transactions
- Use Query Builder or Model methods; enable
use App\Models\BaseModelwhich handles UTC conversions. - Always call
helper('utc')when manipulating timestamps. - Wrap multi-table changes in
$this->db->transStart()/$this->db->transComplete()and checktransStatus()to abort if false. - Run
checkDbError()(existing helper) after saves when manual queries are necessary.
Service Helpers & Libraries
- Encapsulate complex lookups (ValueSet, encryption) inside
app/Librariesor Traits. - Reuse
App\Libraries\Lookupsfor consistent label/value translations. - Keep shared logic (e.g., response formatting, JWT decoding) inside Traits and import them via
use.
Testing & Coverage
- Place feature tests under
tests/Feature, unit tests undertests/Unit. - Test class names should follow
ClassNameTest; methods followtest<Action><Scenario><Result>(e.g.,testCreatePatientValidationFail). - Use
FeatureTestTraitandCIUnitTestCasefor API tests; preferwithBodyFormat('json')->post()flows. - Assert status codes: 200 for GET/PATCH, 201 for POST, 400 for validation, 401 for auth, 404 for missing resources, 500 for server errors.
- Run targeted tests during development, full suite before merging.
Documentation & API Sync
- Whenever a controller or route changes, update
public/paths/<resource>.yamland matchingpublic/components/schemas. Add tags or schema refs inpublic/api-docs.yaml. - After editing OpenAPI files, regenerate the bundled docs with
node public/bundle-api-docs.js. Checkpublic/api-docs.bundled.yamlinto version control. - Keep the controller-to-YAML mapping table updated to reflect new resources.
Routing Conventions
- Keep route definitions grouped inside
$routes->group('api/<resource>')blocks inapp/Config/Routes.php. - Prefer nested controllers (e.g.,
Patient\PatientController) for domain partitioning. - Use RESTful verbs (GET: index/show, POST: create, PATCH: update, DELETE: delete) to keep behavior predictable.
- Document side effects (snapshots, audit logs) directly in the corresponding OpenAPI
pathsfile.
Environment & Secrets
- Use
.envas the source of truth for database/jwt settings. Do not commit production credentials. - Sample values are provided in
.env; copy to.env.localor CI secrets with overrides. JWT_SECRETmust be treated as sensitive and rotated via environment updates only.
Workflows & Misc
- Use
php spark migrate/migrate:rollbackfor schema changes. - For seeding or test fixtures, prefer factories (Faker) seeded in
tests/Supportwhen available. - Document major changes in
issues.mdor dedicated feature docs underdocs/before merging.
Security & Filters
- Apply the
authfilter to every protected route, and keepApiKeyor other custom filters consolidated underapp/Filters. - Sanitize user inputs via
filter_var,esc()helpers, or validated entities before they hit the database. - Always use parameterized queries/Model
save()methods to prevent SQL injection, especially with legacy PascalCase columns. - Respond 401 for missing tokens, 403 when permissions fail, and log sanitized details for ops debugging.
Legacy Field Naming & ValueSets
- Databases use PascalCase columns such as
PatientID,NameFirst,CreatedAt. Keep migration checks aware of these names. - ValueSet lookups centralize label translation:
Lookups::get('gender'),Lookups::getLabel('gender', '1'),Lookups::transformLabels($payload, ['Sex' => 'gender']). - Prefer
App\Libraries\Lookupsorapp/Traits/ValueSetTraitto avoid ad-hoc mappings.
Nested Data Handling
- For entities that carry related collections (
PatIdt,PatCom,PatAtt), extract nested arrays before filtering and validating. - Use transactions whenever multi-table inserts/updates occur so orphan rows are avoided.
- Guard against empty/null arrays by normalizing to
[]before iterating.
Observability & Logging
- Use
log_message('info', ...)for happy-path checkpoints and'error'for catch-all failures. - Avoid leaking sensitive values (tokens, secrets) in logs; log IDs or hash digests instead.
- Keep
writable/logsclean by rotating or pruning stale log files with automation outside the repo.
Final Notes for Agents
- This repo has no UI layer; focus exclusively on REST interactions.
- Always pull
public/api-docs.bundled.yamlin after runningnode public/bundle-api-docs.jsso downstream services see the latest contract. - When in doubt, align with existing controller traits and response helpers to avoid duplicating logic.