# 055 — Make `ottyl_*` reserved params usable in dashboard (PromQL) queries, not just filters ## Summary Three reserved params exist, all hardcoded in the filter compiler: `ottyl_seq` (ingest sequence number), `ottyl_ts` (ingest timestamp, supports relative/absolute values), and `ottyl_fmt` (auto-detected `json`/`logfmt`/`none`) — defined at `src/engine.rs:773-775, 795, 806, 824`, grammar comments at `src/filter_parser.pest:7-9`, docs at `webapp/src/components/Docs.tsx:105-106, 375` ("reserved metric names present on every log line, so they're always filterable without declaring anything"). Alert conditions restrict to the same three plus `regex()`/`json[field]` (`src/alerts.rs:3, 199-225`). These already work end-to-end for the filter/query bar syntax used by `GET /api/logs`, `GET /api/timeline`, and alert conditions — that part of "accessible parameters for queries" is true today. What's missing is dashboards: PromQL-style panel queries (`src/promql.rs`, `evaluate_promql` in `src/engine.rs:1562-1601`) have no special case for `ottyl_*` anywhere. A PromQL query resolves `metric_name` via `bucket_values(index, &parsed.metric_name)` (`src/engine.rs`, called from `evaluate_promql`), which only returns data for names backed by a `DataValue::MetricDeclaration` and bucket entries created through `POST /api/metrics`. `ottyl_*` values are computed line-locally at filter-compile time and never written into declared- metric buckets, so `sum(ottyl_seq)` or similar in a dashboard panel would silently return an empty series rather than real data. Separately, `ottyl_*` never appear in `GET /api/params` (`src/api.rs:281-287` → `EngineQuery::ParamDeclarations` → `param_declarations_range`, `src/engine.rs:1340-1355`), since that only lists explicit `ParamDeclaration` index entries and `ottyl_*` never create one. `FilterBar.tsx` (`webapp/src/components/FilterBar.tsx:36-41`) has no autocomplete/suggestion list at all today (plain text input), so there's nothing currently excluding them from UI suggestions either — but if param suggestions are ever added, this gap should be kept in mind. ## Scope - Add a special case in `evaluate_promql`/`src/promql.rs` (analogous to the filter compiler's handling at `src/engine.rs:773-775`) that synthesizes a virtual bucket series for `ottyl_seq`/`ottyl_ts`/ `ottyl_fmt` metric names from the ingested-log index, instead of requiring a `MetricDeclaration`. Decide and document in the PR how `ottyl_fmt`'s categorical string values map onto a numeric time series (e.g. count-by-value, or reject it from PromQL numeric aggregation with a clear error, since `ottyl_seq`/`ottyl_ts` are naturally numeric but `ottyl_fmt` is not). - Decide whether `ottyl_*` should appear in `GET /api/params` (or a parallel listing) so dashboard panel builders in the webapp can discover them without prior knowledge of the reserved names; if yes, extend `param_declarations_range` or add a small hardcoded merge at the API layer rather than forcing them into the index as real `ParamDeclaration` entries (they aren't user-declared and shouldn't be deletable via ticket 051's/052's params-removal paths). - Update `README.md`/`Docs.tsx` to state whether `ottyl_*` is usable in dashboard queries, once implemented (or explicitly document it as filter-only, if the numeric/categorical mismatch above makes broad PromQL support impractical for `ottyl_fmt`). ## Acceptance criteria - A dashboard panel using a PromQL query over `ottyl_seq` or `ottyl_ts` (e.g. `count(ottyl_seq)` or a rate over `ottyl_ts`) returns real data from ingested logs, verified with a seeded fixture. - The behavior for `ottyl_fmt` in a PromQL query is either implemented (with a stated value-to-series mapping) or explicitly rejected with a clear, documented error — not a silent empty result. - `GET /api/params` behavior for `ottyl_*` is decided and documented; if included, they're clearly marked as reserved/non-deletable rather than indistinguishable from user-declared params. ## Depends on None (independent of the params-removal tickets, but note the non-deletable requirement above if `ottyl_*` end up listed alongside regular params).