# Adom Safety & Health Program

A web app that runs the entire safety program for an Adom-style facility — policy reader, periodic-task tracker, incident reporting, OSHA recordkeeping, lockout/tagout register, hazard communication, and more — all backed by a SQLite database, a Google Chat reminder daemon, and an audit log of every mutating action.

## Live URL

**[r1fapjfs5ruv.adom.cloud](https://r1fapjfs5ruv.adom.cloud/)** — auto-deployed from `origin/main` every 2 minutes via watchdog cron on a dedicated `default-light` service container (`drew2-service-smokey`).

## What's in it

The app has 16 left-nav tabs, each handling one slice of the safety program:

- **Dashboard** — open obligations, overdue tasks, open CAPAs, high risks, upcoming compliance
- **Policy** — five OSHA-aligned sub-programs (main program + EAP + HazCom + LOTO + PPE), versioned on every edit. Internal toggle for the assembled binder-ready full document.
- **Periodic Tasks** — recurring obligations with cadence in days. "Log completion" rolls next-due forward.
- **Incidents** — auto-numbered `INC-YYYY-####`. Root cause, corrective actions, OSHA recordable flag, CSV export.
- **Corrective Actions** — ISO 9001-style CAPA register sourced from incidents / inspections / risks / audits.
- **Risk Register** — 5×5 likelihood × severity heat map with current and residual scoring.
- **Inspections** — template-driven checklists. Pass/fail/N/A per item; failure auto-creates a 14-day-due CAPA.
- **Permits-to-work** — hot work, confined space, electrical, line break. Approval chain + expiry reminders.
- **Chemical Exposures** — per-employee × per-chemical ledger with 30-year retention per HazCom.
- **OSHA Forms** — printable HTML for Form 300 / 300A / 301. Browser → File → Print → PDF.
- **Chemicals & SDS** — inventory with GHS hazard pills. New chemicals trigger a PubChem auto-lookup (CAS / SDS / hazards) → admin gets a Google Chat ping with a "Review in app" button.
- **Equipment / LOTO** — 29 CFR 1910.147. Energy sources, written procedure per machine, audit cadence.
- **PPE Assessments** — per-work-area hazard assessment + required PPE. Annual review reminder.
- **Training Records** — append-only log of who-was-trained-on-what + expiry.
- **Supplies & Ordering** — vendor links per category, bound to the policy that requires each item.
- **Audit Log** — every mutating action stored with actor + before/after JSON. Tamper-evident.

A QR-tag scan landing at `/scan/eq/:id` shows quick-action buttons for any equipment record.

## What's special

**Hourly Google Chat TODO digest.** A single Card v2 message lists every item that needs attention right now: due tasks, open CAPAs, expiring permits, open incidents, chemicals missing CAS/SDS, equipment missing LOTO procedures, PPE assessments past review. The card is sent **only when the list changes** and **at most once per hour** (top of hour). Completing items in the app rolls into the next hourly digest — no immediate ping. The dedup state is a single `reminder_digest` row keyed on a SHA-256 hash of the canonical TODO set.

**PubChem auto-lookup with Google Chat approval.** Adding a new chemical triggers a fire-and-forget PubChem PUG-REST call that proposes CAS / IUPAC name / GHS signal word / H-codes / Sigma-Aldrich SDS URL. Admin gets a Google Chat ping with a "Review in app" button that lands them on a per-field accept/reject modal. Daily quota cap (80,000 req/day) and 5-req/sec pacer protect against tripping PubChem's published rate limits.

**34-check Puppeteer e2e suite** runs against the live URL — every route, every CRUD round-trip, OSHA exports, QR-scan API, audit-log capture. Asserts no JS console errors and no failed network requests.

**Audit log captures everything.** Every create / update / delete writes a row with actor email, action type, entity ID, IP, user-agent, request id, and full before-and-after JSON snapshots. Page versions are kept indefinitely for inspector review.

**App-creator compliant UI.** Dark theme always (`#0d1117` page / `#161b22` panels), Familjen Grotesk + Satoshi + JetBrains Mono fonts, monochrome white SVG icons, frosted-glass cards, hover-delayed tooltips on every button (500ms, body-appended `position:fixed` per `human-ui-patterns §1d`), `GET /api/state` + `GET/POST /api/console` + dev-gated `POST /api/eval` for AI drivability, fixed Adom top-header bar on every page.

## Architecture

```
Service container (drew2-service-smokey, default-light)
├── Express + SQLite    safety-app server, port 8090, public proxy URL
├── reminder-daemon     ticks every 60s, fires hourly TODO digest to Google Chat
└── cron watchdog       pulls origin/main every 2 min and bounces processes on change
```

A `.env` file at `/etc/safety-app.env` holds the GChat webhook URL, public base URL, and (optional) `SHUTDOWN_TOKEN`.

## Repo

`https://github.com/adom-inc/safety-app` (private, `adom-inc` org). Watch the [demo walkthrough video](https://github.com/adom-inc/safety-app/blob/main/demo/safety-app-walkthrough.mp4) for a 79-second tour of every left-nav tab.

## Install on a fresh service container

```bash
# 1. Provision the container (only if you don't have one)
adom-cli carbon user repo-create service-safety
adom-cli carbon containers create --image-id 69b43b3e58d13e5ce628cdc5 \
  --repo-id <repo-id> --class small

# 2. SSH in and run the idempotent provisioner from the repo
ssh <slug>@adom.cloud
git clone git@github.com:adom-inc/safety-app.git /home/adom/safety-app
sudo tee /etc/safety-app.env > /dev/null <<EOF
GCHAT_WEBHOOK_URL='https://chat.googleapis.com/v1/spaces/.../messages?key=...&token=...'
PUBLIC_BASE_URL='https://<port-mapping-host>.adom.cloud'
GCHAT_WEBHOOK_NAME='gchat-safety'
EOF
sudo chown adom:adom /etc/safety-app.env && sudo chmod 0600 /etc/safety-app.env
bash /home/adom/safety-app/scripts/provision-service-container.sh

# 3. Add a port mapping for 8090
adom-cli carbon containers port-add <slug> --port 8090
```

The watchdog cron pulls `origin/main` every 2 minutes, so future updates ship with `git push`.

## Authored with

Claude Code (Opus 4.7, 1M-context).
