Read this when changing persistent roles, user role assignments, inventory items, or their API and navigation contracts.
PHEngine uses a persistent role catalog for authorization and provides a persistent Inventory Monitor demo. Role IDs are stable authorization keys; display labels are editable metadata and must not be used as permission checks.
The roles table contains a numeric id, a unique non-empty name, and an editable description. The initial catalog reserves:
| ID | Initial name | Purpose |
|---|---|---|
1 |
Administrator | User, configuration, and role administration. |
2 |
Inventory operator | Inventory Monitor access and item CRUD. |
Users store a deduplicated PHP-serialized array of integer role IDs. Session and user-management DTOs expose those IDs; role-management responses expose {id, name, description}. Password hashes and serialized persistence fields never leave the API.
Only role ID 1 may manage the role catalog or users. Role deletion is rejected for role ID 1 and for any role assigned to a user; assignment checks and user mutations use the existing transaction and row-locking safeguards.
The inventory_items table stores:
| Field | Contract |
|---|---|
id |
Immutable numeric identifier. |
sku |
Trimmed, non-empty, unique stock-keeping unit. |
name |
Trimmed, non-empty item name. |
quantity |
Non-negative integer quantity. |
reorder_level |
Non-negative integer threshold. |
Inventory endpoints return safe item DTOs only. Role ID 2 is required for reads and mutations; role ID 1 does not implicitly grant inventory access. Mutations require the session CSRF token. Duplicate SKUs and invalid numeric values are rejected with safe JSON errors. Each successful create, update, or delete publishes a shared notification linked to /inventory; notification persistence is non-blocking and does not change the successful mutation result.
The route registry owns these endpoints:
/api/v1/admin/roles and /api/v1/admin/roles/@id for role catalog CRUD./api/v1/inventory/items and /api/v1/inventory/items/@id for inventory CRUD.The administration menu uses role ID 1. The retained Blank plugin publishes Inventory Monitor at /inventory using role ID 2; it does not register volatile role names. Frontend guards improve navigation only and never replace server-side authorization.
scripts/migrate-role-catalog.php is an explicit CLI migration. It creates missing role and inventory tables, inserts missing seed records without overwriting edited labels, and transactionally converts legacy serialized role names to numeric IDs. It maps admin to ID 1, demo to ID 2, preserves existing integer IDs, and creates catalog records for other legacy names before replacing assignments.
The migration is idempotent, is not run during application boot, and must be reviewed before being run against a database. Malformed serialized role data must fail clearly rather than being discarded.
This demo does not represent a production inventory subsystem and does not provide role hierarchies, permission matrices, stock-movement history, audit trails, or multi-warehouse inventory. Role IDs are immutable even when their names and descriptions change.