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