From b70b4d699100f7e3d1952bc7db310689b3843194 Mon Sep 17 00:00:00 2001 From: "b.peck" Date: Thu, 16 Jul 2026 14:49:09 -0500 Subject: [PATCH] =?UTF-8?q?Add=20ONBOARDING.md=20=E2=80=94=20dev=20handoff?= =?UTF-8?q?=20guide=20(setup,=20verify=20loop,=20browser=20harness,=20gotc?= =?UTF-8?q?has,=20current=20state)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- ONBOARDING.md | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 ONBOARDING.md diff --git a/ONBOARDING.md b/ONBOARDING.md new file mode 100644 index 0000000..dfa8a6b --- /dev/null +++ b/ONBOARDING.md @@ -0,0 +1,160 @@ +# ONBOARDING — PrimeBAT Alarm Analysis Dashboard + +Getting-started guide for a developer picking up this Ignition 8.3 Build-a-Thon +project mid-build. Pairs with two other docs: + +- **[README.md](README.md)** — the docker stack, gateway-as-files layout, provisioning. +- **[CONTRACT.md](CONTRACT.md)** — the *frozen build contract*. This is the source of + truth for every prop name, binding pattern, session prop, script signature, style + class, and hard rule. **Read it before editing any view.** + +--- + +## 1. What this is + +A single Perspective dashboard (`PrimeControls/Dashboard`) that answers "is my alarm +system healthy, and what do I fix first?" from ISA-18.2 metrics. All data comes from +`system.alarm.queryJournal` / `queryStatus` (no SQL, no tag bindings) via one script +package, `PrimeControls.{calc, fmt, alarms}`. One journal fetch per refresh produces a +"bundle" that every tab/popup binds against. + +Gateway state is version-controlled **files** under `ignition/gateway/` (see README) — +you edit JSON/Python on disk and tell the gateway to reload; there is no separate +"deploy." + +## 2. Get running (5 min) + +```bash +docker compose up -d # gateway :8088 (admin/password) + MariaDB +curl -s http://localhost:8088/StatusPing # {"state":"RUNNING"} when ready (~1-2 min) +``` + +The committed gateway files already contain the provisioned DB connection, alarm +journal, API token config, and simulator tags — a plain `up -d` restores a working +gateway. Only run `python3 tools/provision.py mint-token && … provision` if you rebuild +from a *factory-fresh* gateway (README "Reset" section). The alarm simulator timer script +is a Designer-side step — see [test-data/README.md](test-data/README.md); without it the +journal is empty and every chart shows its empty state. + +Open the dashboard: + +## 3. The edit → reload → verify loop + +Everything under `ignition/gateway/projects/PrimeBAT/` is a plain file. After editing: + +```bash +python3 tools/lint_project.py # MUST exit 0 (banned strings, Jython, schema) +python3 tools/provision.py scan-projects # hot-reload project resources into the gateway +# (use scan-config instead after editing config resources or tags) +sleep 5; docker logs buildathon-ignition --since 1m 2>&1 | grep -iE "error|exception|traceback" +curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8088/data/perspective/client/PrimeBAT +``` + +`lint_project.py` is the gate — it enforces the CONTRACT's banned strings (`SELECT`, +`[default]`, `BuildathonSim`, f-strings, …), resource.json boilerplate, and style-class +references. Keep it at **0 failures / 0 warnings**. + +## 4. Unit tests (pure data-layer logic) + +`PrimeControls.calc` and `PrimeControls.fmt` are written to run under **both** Jython 2.7 +(in the gateway) and CPython 3 (for pytest). `tests/conftest.py` imports them straight +from the project's `code.py`, so tests exercise the real gateway code. + +```bash +pip install pytest # not vendored +python3 -m pytest -q tests/ # 31 tests (calc + fmt) +``` + +`PrimeControls.alarms` is the gateway adapter (calls `system.alarm.*`) and is **not** +unit-tested — verify it live (next section). + +## 5. Browser verification harness (no Designer needed) + +You can render the live Perspective client headless and screenshot it — how the tabs and +popups in this repo were actually verified (the schema lint can't catch render-time bugs). + +Chromium needs a few system libs. If you have sudo: `npx playwright install --with-deps +chromium`. Without sudo (WSL2 dev boxes), fetch and extract them locally: + +```bash +mkdir -p /tmp/pw && cd /tmp/pw && npm init -y && npm install playwright +npx playwright install chromium +mkdir libs && cd libs +apt-get download libnspr4 libnss3 libasound2t64 libasound2-data +for d in *.deb; do dpkg-deb -x "$d" root; done +export LD_LIBRARY_PATH="$PWD/root/usr/lib/x86_64-linux-gnu" # then run node from /tmp/pw +``` + +Minimal probe (`/tmp/pw/probe.js`) — load, wait for the bundle to fetch, screenshot, +capture console errors: + +```js +const { chromium } = require('playwright'); +(async () => { + const b = await chromium.launch(); + const p = await b.newPage({ viewport: { width: 1600, height: 1000 } }); + p.on('console', m => m.type() === 'error' && console.log('ERR', m.text().slice(0, 200))); + p.on('pageerror', e => console.log('PAGEERR', e.message.slice(0, 200))); + await p.goto('http://localhost:8088/data/perspective/client/PrimeBAT', { waitUntil: 'networkidle' }); + await p.waitForTimeout(11000); // onStartup sets range -> bundle fetch -> render + await p.screenshot({ path: 'dash.png' }); + // click a tab: await p.getByText('Bad Actors').first().click(); await p.waitForTimeout(4000); + await b.close(); +})(); +``` + +`node probe.js` → inspect `dash.png`. A live gateway-side data probe (runs the real +`getDashboardBundle` against the journal) lives at `ignition/gateway/projects/SimHarness/ +ignition/script-python/probe/` — enable the `ProbeTick` timer, scan, read +`ignition/gateway/projects/.probe/out.json`. + +## 6. Gotchas that already bit us (all in CONTRACT.md, repeated here) + +- **Jython 2.7 in all gateway scripts/transforms:** `%` formatting only (no f-strings), + no `typing`/`statistics`, every inline-script line starts with a tab. Wrap any + `system.*` / `PrimeControls.alarms.*` call in a **bare `except:`** — Java exceptions + bypass `except Exception`. Every transform must tolerate `null`/empty (the bundle can + be `None`). +- **A view cannot nest sub-views inside its own folder.** `Dashboard` is a view, so + Header/FilterBar had to move to `PrimeControls/Shell/` — nesting them under + `Dashboard/` made them resolve as "view does not exist." Put shared/child views under + a *pure folder* (`Tabs/`, `Components/`, `Popups/`, `Shell/`). +- **Input params must be `persistent: false`.** With `persistent: true`, Perspective + bakes the last-received value into `view.json` on render — the live bundle (with sim + tag paths) got serialized into a tab and broke lint. Parents/openers always supply + these params, so persistence is never needed. +- **No SQL, no tag bindings, no `journalName=` literals, no docked views.** All resources + under `PrimeControls/` namespaces. +- The gateway writes runtime churn to `ignition/gateway/config/ignition/tags/ + valueStore.idb` and the trial-clock system properties — don't commit those. (Worth + adding to `.gitignore`.) + +## 7. Where things are + +| Path | What | +| --- | --- | +| `…/views/PrimeControls/Dashboard/view.json` | Main view: onStartup range init, `custom.bundle` binding, tab embeds | +| `…/views/PrimeControls/Shell/{Header,FilterBar}` | Health score + KPI ribbon + date range; filter bar | +| `…/views/PrimeControls/Tabs/{Overview,Analysis,BadActors,Journal}` | Tab content (embedded, tab-visibility bound) | +| `…/views/PrimeControls/Popups/{HealthScore,ShiftReport,AlarmDetail,EventDetail}` | Popups (IDs are constants — see CONTRACT) | +| `…/views/PrimeControls/Components/*` | Shared components (KpiCard, SourceRow, HeatmapCell, …) | +| `…/script-python/PrimeControls/{calc,fmt,alarms}/code.py` | Data layer (calc/fmt pure; alarms = gateway adapter) | +| `tools/lint_project.py`, `tools/provision.py` | Lint gate; gateway provisioning/reload | +| `tests/` | pytest for calc + fmt | + +## 8. Current state & what's next + +**Done (Phase 2, committed):** all 4 tabs + 4 popups built; Overview timeline and the +Bad Actors ranked "Top Sources" table are **browser-verified**. Lint clean. + +**Not yet live-verified:** Analysis, Journal, and the 4 popups were schema-verified and +reload-clean but never rendered/clicked — do a browser pass (section 5) before trusting +them. + +**Next phase — the Header.** It renders now (after the Shell relocation) but is slated +for rework, and a benign `React.cloneElement … null` console warning still needs chasing +(the KPI ribbon / dropdowns are the suspects, since it fires even with the header +painting correctly). + +See [plan.md](plan.md) for the overall design and the remaining "build if time allows" +ideas (compare-to-prior-period deltas, native drawing gauge, shift report polish).