README.md
Language Markdown3.6 KB79 lines
# Meridian Ops BoardAn operations dashboard and daily AI briefing for a small print and displayshop. It pulls orders, CRM leads and tasks, EOS to-dos, a content tracker,meeting action items and the shared inboxes into one page per person, and hasa language model write each person a morning briefing from the same data.**Everything about the company is fictional.** Meridian Display Co., its staff,its clients, its orders and every piece of data in this repository areinvented. The system itself is real: it was designed and built withAI-assisted development and ran in daily use, and this is a rebuild of it withsample data.## What it shows- **The people board.** Who is loaded and who is not, per person, across everysource. A person no source covers reads "not tracked", never zero.- **My Day.** Each person's own orders, tasks, to-dos and meeting commitments,with a private task list only they can read.- **The shared inbox.** Every role mailbox in one view, with role-gated replyand send, a confirmation step and an audit record.- **The morning briefing.** A distilled payload goes to a language model, whichwrites a company briefing and a personal day plan for each person, tiered byrole.- **Onboarding.** A training plan and an end-of-day note for a new hire, visibleonly to the people who should see it.- **Data health.** Which sources answered, which failed, and what nothing covers.## How it is builtA pnpm monorepo in TypeScript, with one Python script for the briefing.| Part | Where ||---|---|| API clients for Plinth, Bitrix24, Ninety, Google Sheets, Fathom and Gmail | `packages/` || Collect, view model and page rendering | `apps/dashboard/` || Cloudflare Worker: cron sync, Access-verified pages, the inbox, D1 | `apps/worker/` || Rosters, stage taxonomies, grants and other human-edited config | `config/` || The briefing, the only place a model is called | `analysis/brief.py` |The design rules the code follows, starting with "zero, null and broken mustnever look the same", are in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).## Running itNode 22.5 or later and pnpm 9. The D1 store tests run on `node:sqlite`, whicholder Node does not have; below 22.5 they skip with a warning rather than fail.```bashpnpm installpnpm test # 1,168 TypeScript and 49 Python cases, offlinepnpm typecheckpnpm dashboard --fixtures # writes dashboard.html from sample data```CI (`.github/workflows/ci.yml`) runs install, typecheck and test on Node 22.Live runs need credentials in the process environment; see`credentials.example.sh`. There is deliberately no `.env` reader. Thebriefing (`analysis/brief.py`) calls a local OpenAI-compatible server at`http://127.0.0.1:1234/v1` by default. Pointing `ANTHROPIC_BASE_URL` at theAnthropic API switches it to that API, which needs `ANTHROPIC_API_KEY`.## Deploying the Worker```bashcd apps/worker # once per accountwrangler kv namespace create DASHBOARD # and again with --previewwrangler d1 create meridian-checkinswrangler d1 migrations apply meridian-checkins --remotewrangler secret put NINETY_TOKEN # also BITRIX_WEBHOOK_URL, PLINTH_API_KEYwrangler deploy```Paste the printed KV and D1 ids into `wrangler.jsonc`. Gmail credentials areWorker secrets too (`pnpm inbox:auth` mints and stores a mailbox's token).Put a Cloudflare Access application in front of the `workers.dev` URL and itspreview URLs, then set `ACCESS_TEAM_DOMAIN` and `ACCESS_AUD` in`wrangler.jsonc`: until both are set the Worker refuses every request.`pnpm morning` syncs, writes the briefings and pushes the pages to the Worker.