**Tx OS** — a bilingual (Arabic/English, RTL/LTR) full-stack internal web platform styled as an OS-like interface with glassmorphism aesthetics. Built as a pnpm monorepo.
- **OS Home Screen**: live clock status bar (per-user clock style: full / digital / digital-no-seconds / analog / minimal, picker in status bar), app grid, bottom dock
- **Admin Panel**: CRUD for apps, services, users (admin role required). Delete dialogs show a dependency warning on the FIRST click using count fields (`groupCount`/`restrictionCount`/`openCount` on apps, `orderCount` on services, `noteCount`/`orderCount`/`conversationCount`/`messageCount` on users) returned by the list endpoints (`GET /api/admin/apps`, `GET /api/services`, `GET /api/users`); the lazy 409 conflict response from `DELETE /api/{apps,services,users}/:id` (with `?force=true` to override) remains as a safety net.
- **Executive Meetings (Phase 2)**: bilingual full-stack module under `/executive-meetings` with 9 sections — Schedule (centered cells, attendees widest column, RTL-locked column order # / الاجتماع / الحضور / الوقت), Manage Meetings (CRUD with attendee replace, transactional), Change Requests (submit / withdraw, supports `meetingId=null` for create-suggestions), Approvals (approve/reject with review notes), Tasks (CRUD with assignee status updates — assignees and mutators can change status, only mutators can delete), Notifications (per-user feed; meeting/request/task events fan out via `recordExecutiveMeetingNotifications` to both `executive_meeting_notifications` and the global `notifications` bell, then broadcast via Socket.IO `notification_created` per-user + `executive_meeting_notifications_changed` globally; approvers also receive a best-effort email side-channel via `sendExecutiveMeetingEmail` that logs an outbox entry until SMTP is wired up), Audit Log (full action chain, admin role required), PDF (window.print export), Font Settings (per-user + global scope, family/size/weight/alignment with live preview). RBAC enforced via 5 role sets (READ/MUTATE/APPROVE/REQUEST/ADMIN_AUDIT) and a `makeRequireRoles` middleware factory; `/api/executive-meetings/me` returns `{userId, roles, canRead, canMutate, canApprove, canSubmitRequest, canViewAudit}`. Every mutation (meeting/request/task/font CRUD) wraps the DB write **and** the audit-log insert in the same `db.transaction(...)` so audit entries cannot drift from state. Routes use `router.param("id")` with `next("route")` to handle path-to-regexp 8 (no inline `:id(\\d+)` support).
- i18n locale files: `artifacts/tx-os/src/locales/ar.json` and `en.json`
- Default receivers group is named **Tx** (renamed from legacy "TeaBoy"); a one-time migration in the seed script renames any pre-existing legacy group on next run.
- **Rich-text columns are PostgreSQL `text` (no length cap).** `executive_meetings.title_ar`, `executive_meetings.title_en`, and `executive_meeting_attendees.name` were widened from `varchar(500)` / `varchar(255)` to `text` to hold sanitized Tiptap HTML. The schema declarations live in `lib/db/src/schema/executive-meetings.ts`.
- **`pnpm --filter @workspace/db run push-force` must run in every environment** (dev, staging, production) after deploying schema changes. Dev is covered automatically by `scripts/post-merge.sh`. Staging and production must run the same command on each deploy so their `title_ar` / `title_en` / `name` columns match the code; otherwise long rich-text saves will be rejected by the old varchar limits.
- **One-time pre-push cleanup (run BEFORE `pnpm --filter @workspace/db run push-force` in any environment that has not been pushed since these constraints were added).** Two pieces of legacy data block the push because the old DB never enforced the constraints the schema now declares:
1. Duplicate rows in `app_permissions` (the schema declares a composite primary key on `(app_id, permission_id)`).
2. Orphan rows in `executive_meeting_notifications` whose `meeting_id` no longer exists (the schema declares `ON DELETE CASCADE`, which was never enforced because the FK was missing).
Run this idempotent SQL once per environment before the push:
```sql
BEGIN;
-- Collapse duplicate app_permissions rows to one per (app_id, permission_id)
CREATE TEMP TABLE app_permissions_dedup AS
SELECT DISTINCT app_id, permission_id FROM app_permissions;
DELETE FROM app_permissions;
INSERT INTO app_permissions (app_id, permission_id)
SELECT app_id, permission_id FROM app_permissions_dedup;
-- Drop notifications whose meeting was already deleted
DELETE FROM executive_meeting_notifications n
WHERE n.meeting_id IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM executive_meetings m WHERE m.id = n.meeting_id);
COMMIT;
```
Then run `pnpm --filter @workspace/db run push-force`. The push has been verified end-to-end against the dev database and is idempotent on subsequent runs.