Ops Nervous System¶
Status: Design complete; Phase 0 pending Nisarg. Owner: Dan (app layer), Nisarg (replica infrastructure, one-time) Last updated: 2026-04-18
Vision¶
Every person at Scott Recycling always knows their numbers and where they should be — in real time, through their phone. Dan's Personal Assistant becomes company-aware and delivers specific, grounded, actionable briefs. A single live data surface powers both systems; the app layer evolves fast without ever touching Odoo production.
The shorthand: prod Odoo is the museum — curated, protected, slow-moving. The app layer is the workshop — fast, messy, and owned by Dan. A live read-only replica is the clean window between them.
Architecture — three layers¶
Layer 1: Replica (Nisarg-owned, one-time setup)¶
A PostgreSQL streaming read replica of the Odoo production database, hosted on EXP.
- Primary stays on prod (untouched).
- Replica runs as a second Postgres cluster on EXP, on port 5433.
- WAL streams from prod to replica within milliseconds.
- Physically read-only at the engine level — no query from any app can write anything, ever.
- All Odoo tables mirrored — not a curated subset. Everything is available.
Two Postgres roles created on the replica:
dan_assistant_reader—SELECTon all tables. For the Personal Assistant.scoreboard_reader—SELECTonly on operational tables (pickups, boxes, KPIs, employees, attendance, trucks, etc.). For the newsr-pulseemployee-facing app.
Layer 2: Reporting DB (Dan-owned, writable)¶
The existing scott_reporting database on EXP is repurposed as a derived/aggregated data store:
- Daily/weekly/monthly employee rollups (pre-aggregated; queries are instant)
- Trend tables and 30/60/90-day baselines
- Cross-system joins (Odoo + Health ERP + Financial + Superstore)
- Targets per role/metric
Populated by scheduled ETL jobs Dan writes: Replica → ETL → reporting DB → apps.
Layer 3: Apps (Dan-owned, fast-moving)¶
Two independent applications, both reading the replica:
- Personal Assistant (
/opt/personal-assistant) — existing Django app. Gains a 5th adapter viadan_assistant_reader. Its 6:30am / 1pm / 9pm briefs become company-aware. sr-pulse(new,/opt/sr-pulse) — employee-facing Django app. Reads viascoreboard_reader. Delivers mobile PWAs, scoreboards, and nudges.
The two apps never share a process or a Postgres role. Security boundary enforced architecturally.
Key principles¶
- Real-time during the day, not end-of-shift digests. Production employees need live PWAs that update as they add units — scoreboards that affect behavior while it's happening, not after.
- Targets defined over time, not up front. Pilot launches with count-only views; targets fill in per position as Dan and managers set realistic numbers. Architecture supports no-target, target-defined, and target-revised states without code changes.
- Reports become nudges become products. Don't plan exhaustively; build the ability to create any report/nudge in minutes. Discover value by running things, then codify what works.
- Odoo changes stay Nisarg's. New fields, modified views, workflows — all in Odoo. The app layer never tries to replace Odoo; it consumes and signals.
Scope boundaries¶
In scope (app layer, Dan-owned):
- Reading, computing, deciding, signaling
- Scoreboards, leaderboards, dashboards, PWAs
- SMS, email, PDF exports, webhooks
- LLM summaries, anomaly explanations, Q&A over data
- Cross-system views combining Odoo with other business systems
Out of scope (stays in Odoo, Nisarg-owned):
- Adding new Odoo data types, fields, or models
- Modifying Odoo's UI, forms, reports, or workflows
- Writing back into Odoo records (narrow XML-RPC exceptions if truly needed)
Phased rollout¶
Phase 0 — Replica plumbing (Nisarg, one-time, ~2–3 hours) See Replica Phase-0 Spec.
Phase 1 — First pilot scoreboard (Dan, ~2–3 days)
- Role: sort line techs
- Metric:
boxes_sortedtoday - Primary delivery: mobile PWA each tech opens on their phone or workstation. Live count, updating within seconds as boxes are added in Odoo.
- Secondary delivery: end-of-shift SMS summary.
- Targets are initially count-only; realistic per-shift numbers defined with the warehouse manager during pilot.
Phase 2 — Personal Assistant replica adapter (Dan, ~2 hours)
- 5th adapter added to
/opt/personal-assistant; existing 6:30am / 1pm / 9pm briefs gain live company data.
Phase 3 — Fan-out (Dan, ongoing)
- More scoreboards for more roles: dispatch, sales, office, yard, warehouse.
- Manager-facing digests ("who needs a conversation today").
- Employee-facing PWAs.
Phase 4 — LLM personalization + corrective actions (Dan, ongoing)
- Claude drafts personalized messages grounded in real per-person data.
- Review loop before auto-send; then turn on.
- Pre-curated corrective action menu per role/metric.
Phase 5 — Cross-system reporting (Dan, ongoing)
- Combine replica with Financial, Health ERP, Superstore, Personal Assistant data.
- Unified business dashboard.
Roles¶
- Nisarg — Phase 0 only (replica + roles + brief README). After that, out of the critical path for this initiative. Optionally later: configure
base_automationwebhook triggers for event-driven nudges. - Dan — app layer. Queries, ETL, prompts, delivery, UX, iteration.
- Claude — drafting content, helping design, grounded analysis of replica data.
Success criteria¶
Phase 0 complete when:
psqlasscoreboard_readeragainst the replica connects and reads the expected operational tables.- A row updated on prod appears in the replica within 2 seconds.
- A write attempt as
scoreboard_readeris rejected by Postgres.
Phase 1 complete when:
- A sort line tech has a PWA open showing their live
boxes_sortedcount for the shift, updating within seconds as they add boxes in Odoo. - End-of-shift SMS lands with the final count and a one-line supportive message.
Steady state:
- Dan ships a new report or nudge in under an hour, end to end.
- Nisarg is not on the critical path for any of it.
- Every employee knows their numbers every day.