May 20, 5-6 AM (37)
May 20, 6-7 AM (49)
May 20, 7-8 AM (54)
May 20, 8-9 AM (50)
May 20, 9-10 AM (16)
May 20, 10-11 AM (41)
May 20, 11-12 PM (28)
May 20, 12-1 PM (50)
May 20, 1-2 PM (92)
May 20, 2-3 PM (21)
May 20, 3-4 PM (326)
May 20, 4-5 PM (23)
May 20, 5-6 PM (23)
May 20, 6-7 PM (17)
May 20, 7-8 PM (23)
May 20, 8-9 PM (15)
May 20, 9-10 PM (5)
May 20, 10-11 PM (34)
May 20, 11-12 AM (16)
May 21, 12-1 AM (16)
May 21, 1-2 AM (9)
May 21, 2-3 AM (11)
May 21, 3-4 AM (7)
May 21, 4-5 AM (4)
May 21, 5-6 AM (27)
May 21, 6-7 AM (14)
May 21, 7-8 AM (22)
May 21, 8-9 AM (34)
May 21, 9-10 AM (45)
May 21, 10-11 AM (37)
May 21, 11-12 PM (27)
May 21, 12-1 PM (63)
May 21, 1-2 PM (68)
May 21, 2-3 PM (60)
May 21, 3-4 PM (53)
May 21, 4-5 PM (20)
May 21, 5-6 PM (27)
May 21, 6-7 PM (27)
May 21, 7-8 PM (25)
May 21, 8-9 PM (23)
May 21, 9-10 PM (3)
May 21, 10-11 PM (29)
May 21, 11-12 AM (10)
May 22, 12-1 AM (16)
May 22, 1-2 AM (6)
May 22, 2-3 AM (8)
May 22, 3-4 AM (4)
May 22, 4-5 AM (11)
May 22, 5-6 AM (10)
May 22, 6-7 AM (21)
May 22, 7-8 AM (13)
May 22, 8-9 AM (38)
May 22, 9-10 AM (12)
May 22, 10-11 AM (18)
May 22, 11-12 PM (25)
May 22, 12-1 PM (24)
May 22, 1-2 PM (34)
May 22, 2-3 PM (56)
May 22, 3-4 PM (13)
May 22, 4-5 PM (29)
May 22, 5-6 PM (13)
May 22, 6-7 PM (19)
May 22, 7-8 PM (20)
May 22, 8-9 PM (12)
May 22, 9-10 PM (12)
May 22, 10-11 PM (41)
May 22, 11-12 AM (12)
May 23, 12-1 AM (9)
May 23, 1-2 AM (0)
May 23, 2-3 AM (3)
May 23, 3-4 AM (1)
May 23, 4-5 AM (1)
May 23, 5-6 AM (4)
May 23, 6-7 AM (12)
May 23, 7-8 AM (1)
May 23, 8-9 AM (3)
May 23, 9-10 AM (1)
May 23, 10-11 AM (1)
May 23, 11-12 PM (5)
May 23, 12-1 PM (1)
May 23, 1-2 PM (6)
May 23, 2-3 PM (5)
May 23, 3-4 PM (5)
May 23, 4-5 PM (4)
May 23, 5-6 PM (0)
May 23, 6-7 PM (3)
May 23, 7-8 PM (23)
May 23, 8-9 PM (1)
May 23, 9-10 PM (9)
May 23, 10-11 PM (21)
May 23, 11-12 AM (27)
May 24, 12-1 AM (9)
May 24, 1-2 AM (0)
May 24, 2-3 AM (1)
May 24, 3-4 AM (1)
May 24, 4-5 AM (0)
May 24, 5-6 AM (3)
May 24, 6-7 AM (1)
May 24, 7-8 AM (2)
May 24, 8-9 AM (2)
May 24, 9-10 AM (4)
May 24, 10-11 AM (4)
May 24, 11-12 PM (1)
May 24, 12-1 PM (7)
May 24, 1-2 PM (46)
May 24, 2-3 PM (5)
May 24, 3-4 PM (3)
May 24, 4-5 PM (18)
May 24, 5-6 PM (2)
May 24, 6-7 PM (4)
May 24, 7-8 PM (13)
May 24, 8-9 PM (10)
May 24, 9-10 PM (15)
May 24, 10-11 PM (33)
May 24, 11-12 AM (42)
May 25, 12-1 AM (9)
May 25, 1-2 AM (4)
May 25, 2-3 AM (6)
May 25, 3-4 AM (1)
May 25, 4-5 AM (6)
May 25, 5-6 AM (14)
May 25, 6-7 AM (17)
May 25, 7-8 AM (17)
May 25, 8-9 AM (32)
May 25, 9-10 AM (43)
May 25, 10-11 AM (64)
May 25, 11-12 PM (33)
May 25, 12-1 PM (43)
May 25, 1-2 PM (40)
May 25, 2-3 PM (20)
May 25, 3-4 PM (27)
May 25, 4-5 PM (16)
May 25, 5-6 PM (6)
May 25, 6-7 PM (7)
May 25, 7-8 PM (11)
May 25, 8-9 PM (12)
May 25, 9-10 PM (16)
May 25, 10-11 PM (43)
May 25, 11-12 AM (25)
May 26, 12-1 AM (11)
May 26, 1-2 AM (11)
May 26, 2-3 AM (8)
May 26, 3-4 AM (11)
May 26, 4-5 AM (6)
May 26, 5-6 AM (9)
May 26, 6-7 AM (26)
May 26, 7-8 AM (43)
May 26, 8-9 AM (39)
May 26, 9-10 AM (42)
May 26, 10-11 AM (45)
May 26, 11-12 PM (59)
May 26, 12-1 PM (34)
May 26, 1-2 PM (49)
May 26, 2-3 PM (47)
May 26, 3-4 PM (17)
May 26, 4-5 PM (20)
May 26, 5-6 PM (13)
May 26, 6-7 PM (20)
May 26, 7-8 PM (12)
May 26, 8-9 PM (15)
May 26, 9-10 PM (15)
May 26, 10-11 PM (34)
May 26, 11-12 AM (28)
May 27, 12-1 AM (16)
May 27, 1-2 AM (8)
May 27, 2-3 AM (9)
May 27, 3-4 AM (5)
May 27, 4-5 AM (32)
May 27, 5-6 AM (2)
3,616 commits this week May 20, 2026 - May 27, 2026
cardano-timeseries-io | cardano-tracer: fix bugs and style issues from code review
- Elab.hs: fix copy-paste error in binary arithmetic op elab (rhs hole was
  unified against lhsTy instead of rhsTy); rename evalBinaryArithmethicOpElabProblem
  to evalBinaryArithmeticOpElabProblem (typo)
- Elab.hs: elaborate `metrics` as List Text (was Text); add Str elab case
- Elab/Typing.hs, Resolve.hs, Unify.hs: add List Ty to support metrics type
- Interp.hs: guard avg/min/max against empty instant vector; fix rate to
  error on single-point timeseries instead of dividing by zero
- Interp/Value.hs: use showFFloat in Show instance for Scalar to avoid
  scientific notation in JSON output
- TimeseriesServer.hs: fix minimumRetentionMillis units (seconds → ms);
  remove unused RecordWildCards pragma; align sleep delay with Monitoring.hs
- Acceptors/Utils.hs: align new imports with surrounding import block
cardano-tracer: add node info/startup/state HTTP endpoints to timeseries server
New routes on the timeseries server:
  GET /timeseries/nodes                        — list connected node IDs
  GET /timeseries/node/{id}/info               — NodeInfo + uptimeSeconds
  GET /timeseries/node/{id}/startup            — NodeStartupInfo
  GET /timeseries/node/{id}/state  (RTVIEW)    — sync progress %

The server now receives TracerEnv instead of individual fields so it can
access teDPRequestors, teCurrentDPLock, and teConnectedNodes.

The /state endpoint uses data point key "NodeAddBlock" (the namespace
cardano-node actually stores the NodeState data point under) rather than
"NodeState", which was always empty.
grafana-datasource: add node info/startup/state/uptime query types and dashboard panels
New query types in the datasource plugin:
  nodes       — lists all connected node IDs (used by $node_id variable)
  node-info   — name, protocol, version, commit, start time, uptime
  node-startup — era, slot length, epoch length, KES period
  node-state  — sync progress %

New panels in rtview.json (all repeat by $node_id variable):
  Connected Nodes table, Node Info table, Startup table,
  Sync % stat, Uptime stat

metricFindQuery populates the $node_id query variable automatically
from /timeseries/nodes.

$__from/$__to in timeseries queries are pre-processed to valid
timestamp expressions before template variable expansion.
grafana-datasource: wire up Grafana time picker to query range
Replace hardcoded [now - 1h; now] in all 25 dashboard panels with
[$__from; $__to] so the Grafana time picker controls the window.

Pre-process $__from/$__to in the datasource plugin before getTemplateSrv()
sees them — Grafana treats these as built-in variables and ignores
scopedVars overrides — converting them to epoch + Nms expressions that
the query language interprets as absolute timestamps.
timeseries elab: suggest similar names in Undefined-name error
When a variable is not found in either the local context or the metric
store, the elaborator now appends a "Did you mean: ..." hint by ranking
all candidate names (locals + metrics) by Levenshtein distance and
showing the closest ones (up to 5) within threshold max(1, len/3).

Uses the `edit-distance` library (the canonical Haskell implementation,
also used by GHC for its own "did you mean" diagnostics).
timeseries: add three unambiguous noncanonical arithmetic rules
All three rules fire only when the known type information uniquely
determines the outcome:

  ? - Duration : ?  ->  Timestamp - Duration : Timestamp
    Only Timestamp - Duration exists with Duration on the Sub rhs.
    Enables: \x -> x - 1s  (infer x : Timestamp)

  ? + ? : Duration  ->  Duration + Duration : Duration
    Only Duration + Duration produces Duration.
    Enables: \x -> \y -> m [now; now : x + y]  (infer both : Duration)

  ? - ? : Timestamp  ->  Timestamp - Duration : Timestamp
    Only Timestamp - Duration produces Timestamp via Sub.
    Enables: \x -> \y -> m [x - y; x]  (infer x : Timestamp, y : Duration)

Ordering: A (? - Duration) before C (? - ? : Timestamp) so the more
specific rhsTy=Duration match takes priority. B (? + ? : Duration)
after the existing Duration + Duration : ? rule so the known-both-sides
case is tried first.
timeseries: align HTTP API and JSON wire format with Prometheus
- JSON.hs: rename tag/value to resultType/result; use Prometheus
  resultType names (scalar, vector, matrix); encode timestamps and
  durations as Unix seconds (Double); encode data-point values as
  strings; rename labels->metric and data->values in Instant/Timeseries
- TimeseriesServer: parse query endpoint body as
  application/x-www-form-urlencoded (Prometheus wire format); add GET
  support alongside POST, sharing a single handleQuery helper
- grafana-datasource: update types, toDataFrames, and datasource to
  match new wire format; switch fetch to x-www-form-urlencoded
timeseries: expand elab test suite to ~110 tests
Replaces the 7-test skeleton with a comprehensive suite covering all
elaborator code paths: literals, duration literals, arithmetic
(well-typed and ill-typed), comparisons, boolean operators, let/lambda,
pairs/projections, to_scalar, abs/round, metrics builtin, range
vectors, instant-vector operations, and metric-name resolution (known
metric, undefined name, Levenshtein suggestions).

Notes two elaborator bidirectionality constraints discovered via
test failures:
- `Duration + Duration` requires the result type to be driven by
  a containing expression (tested via `now + (1s + 2s)`).
- `\x -> x + 1` leaves `x`'s type unconstrained; replaced with
  `\x -> now + x` which forces `x : Duration` via the Timestamp+?
  noncanonical rule.
bench | grafana-datasource: error handling, Unit value support
- Parse/eval errors from the server are now surfaced as DataQueryError:
  the banner shows the first line (summary); the full multi-line message
  (source location + caret) is in data.message for the inspector
- Requests go via Grafana's server-side proxy (instanceSettings.url) to
  avoid CORS and host-resolution issues in the browser
- Add Unit value: renders as an empty frame (no data to display)
bench | timeseries: JSON API, Grafana datasource plugin, ci-bench-timeseries profile
**cardano-tracer / cardano-timeseries-io**
- Add `Cardano.Timeseries.JSON` with `ToJSON` instances for `Value`, `Instant`,
  `Timeseries` and `SeriesIdentifier` (orphan module, imported for side-effects)
- Switch `POST /timeseries/query` response from plain text to `application/json`

**cardano-profile**
- Add `timeseries :: Bool` field to `Tracer`
- Add `tracerTimeseries` primitive
- Add `ci-bench-timeseries` profile: 2-node local cluster with timeseries endpoint
  enabled, no shutdown condition, generator runs for 100 000 epochs (effectively
  indefinite) — intended for interactive exploration via Grafana
- Regenerate `all-profiles-coay.json` and `wb_profiles.mk`; update all test fixtures

**Nix**
- `cardano-tracer-service-workbench.nix`: add `timeseries` option → `hasTimeseries`
- `cardano-tracer-service.nix`: add `timeseriesEnable/Host/Port` NixOS options
- `tracer.nix`: wire `profile.tracer.timeseries` → `{epHost, epPort = 3400}`
- `supervisor.sh`: replace hanging `netstat -pltn` with `lsof -nP -iTCP:9001
  -sTCP:LISTEN` for macOS compatibility

**bench/grafana-datasource** (new)
- TypeScript Grafana datasource plugin (`iog-cardanotimeseries-datasource`)
- Sends `POST /timeseries/query` via Grafana server-side proxy (avoids CORS)
- Converts `Value` tagged-union JSON to Grafana `DataFrame[]`
- `docker-compose.yaml` for local development; datasource auto-provisioned at
  `http://host.docker.internal:3400`; Colima-compatible (`extra_hosts`)
timeseries: give the elaborator metric-name awareness for better errors
St now carries availableMetrics :: Set MetricIdentifier, populated at
the call site via metrics store (or Set.empty in metric-free contexts
like the elab test suite).

The fallback Variable case — previously an unconditional metric assumption
— now checks membership in availableMetrics and throws "Undefined name: <v>"
when the name appears neither in the local context nor in the store,
replacing the confusing downstream type-mismatch error that occurred before.
bench | grafana-datasource: provisioned RTView dashboard; enable RTView in ci-bench-timeseries
- Add provisioned Grafana dashboard mimicking RTView's four sections:
  Resources, Blockchain, Leadership, Transactions (26 panels, row-based
  collapsible sections, byte units for memory/mempool panels)
- Pin datasource uid in provisioning so dashboard references are stable
- Mount dashboards directory in docker-compose
- Enable RTView (hasRTView, port 3300) in ci-bench-timeseries profile
  alongside the existing timeseries endpoint