Skip to content

Debugging

Four tools, for four different moments: the debug panel while you’re looking at a rendered frame, noemata validate for a fast schema/reference check as you edit, noemata run when you need to know what a query actually cost, and noemata screenshot when you need to see what it looks like.

The debug panel

Press ⌘B in the app to open it. It’s scoped to the frame you’re looking at, with three tabs:

  • Inspect — every block and expression on the page, in render order. Select one to see its resolved queries and any errors inline, without leaving the panel.
  • Performance — a waterfall of the queries the page issued, so a slow panel is easy to spot against its neighbors.
  • Files — the source files the page’s inspectables resolve to, with a jump-to-source action.

The query timing bar separates elapsed time into five stages. The tooltip and span details show each stage’s duration and share:

  • Client queue — the browser waiting for one of its own permits. Zero unless the deployment sets db.clickhouse.max_client_connections.
  • Server queue — the relayed query waiting for one of the server’s permits, capped by db.clickhouse.max_server_connections. A long wait here says the page dispatched more queries at once than that cap allows; each query on its own may be fast.
  • Database — time the database reports for the statement. A cache hit has no database execution time.
  • Server — the server’s own time on the request, outside the database’s execution of it.
  • Client — time spent compiling the statement, sending the request, receiving the response, and decoding the result. These stages can occur on both sides of the server request.

The panel accumulates across renders, as a browser’s network panel does: a refresh or a filter change adds its queries beside the ones already there. Press Clear in the header to empty the panel and read a single interaction on its own.

This is the fastest loop for “why does this panel show nothing” or “why is this slow” while you’re already looking at the frame.

Validating a frame

noemata validate checks every frame in the workspace against the schema and its own reference names — undefined scalars, unregistered block/expression types, malformed measures — without needing anything running:

Terminal window
noemata validate
  • --online also renders each frame against the live backend and resolves its view tables against the live schema, catching a scalar or table the database can’t actually resolve. It also dry-runs every workflow: each one runs through its pipeline with a capturing emit sink, so @expr/emit projects its records and reports projection errors as usual, but the OTLP collector never receives them. The summary lists any workflow that failed and the record count each one would have emitted.
  • --files <path|glob> narrows the check to the frames a change to those files invalidates, rather than the whole workspace. When combined with --online (or when the installed pre-edit hook fires), the affected workflows dry-run too — workflows whose own file is in the changed set, and workflows whose borrowed frame is transitively affected.
  • --no-overrides / --no-local check the frame as shipped or as a teammate would see it, skipping your own overlay files.
  • --concurrency N renders up to N frame states in parallel (default 1). It only applies to --online (the static pass is already parallel). Above 1, an escaped rxjs or unhandledRejection error is no longer attributed to a specific frame in the report — the engine’s own error boundary still catches and attributes the common cases.

With the Claude Code integration installed, the integrations.installed.claude_code.hooks.validate_frames_on_edit hook runs validation after each edit.

Running a frame

Where validate --online sweeps the whole workspace for errors, noemata run is for working on one dashboard: it renders a single frame or page against the live backend and reports both runtime errors and what each query cost — duration, rows and bytes scanned, server time, peak memory, cache outcome — grouped under the block that issued it.

Terminal window
noemata run services/checkout
noemata run @opentelemetry/views/traces --sql 'SELECT ServiceName, count() AS n FROM traces GROUP BY ServiceName ORDER BY n DESC'

--sql and --expr run semantic SQL or a table-valued expression against the frame’s view directly, with the view’s WHERE, scalars, and timerange applied; --page renders an ad-hoc page against the view without writing anything to the workspace. --param pins a route parameter, and --window overrides the default time range. Every run writes a JSON report and prints its path, plus a run id you can look up in the backend’s own telemetry (SpanAttributes['noemata.run.id']) — useful when the numbers in the report need a closer look than the summary gives.

noemata run disables ClickHouse’s query caches. noemata validate --online leaves them enabled. Use run to compare query timings without cache hits.

Screenshotting a frame

Terminal window
noemata screenshot services/checkout --param ServiceName=checkout --window 24h

Renders the frame in a real browser and writes a PNG to <project>/.local/screenshots/ (or --out). Use it to attach a rendered dashboard to a pull request or an issue, or to give an agent visual evidence of what it authored. The PNG carries a Noemata banner below the frame — the mark, the wordmark and noemata.sh.

run cannot do this: layout is an input to a frame’s own rendering — a plot suspends until it has a measured size — so a headless render has no chart in it. The capture waits for the frame’s render stream to fall quiet, so panels are resolved rather than half-loaded.

Pass --page <file> --keep-browser while iterating on an ad-hoc block. The next screenshot for the same route reuses Chrome and replaces the block in the loaded document without a refresh. Omit --keep-browser on the final capture to close Chrome. Page content travels over CDP, so large block definitions do not become oversized render URLs.

Needs an installed Chrome, Chromium or Edge; see screenshot for the flags and for what happens when there is none.