forked from b.peck/BAT
Add ONBOARDING.md — dev handoff guide (setup, verify loop, browser harness, gotchas, current state)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
160
ONBOARDING.md
Normal file
160
ONBOARDING.md
Normal file
@@ -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: <http://localhost:8088/data/perspective/client/PrimeBAT>
|
||||
|
||||
## 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).
|
||||
Reference in New Issue
Block a user