Add tickets and ideas
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# 093 — Log-line JSON viewer should default to collapsed, not expanded
|
||||
|
||||
## Summary
|
||||
|
||||
When a log line's text parses as a JSON object, the frontend renders it
|
||||
via a collapsible tree view — but every node starts expanded, so a
|
||||
line with a large JSON payload dumps its entire structure into the
|
||||
timeline by default instead of showing a collapsed summary the user
|
||||
opts into expanding.
|
||||
|
||||
## Where
|
||||
|
||||
- `webapp/src/components/LogPreview.tsx:97-107` — `JsonValueView`
|
||||
recursively renders JSON values, delegating objects/arrays to
|
||||
`JsonNode`.
|
||||
- `webapp/src/components/LogPreview.tsx:109-156` — `JsonNode` renders
|
||||
a collapsible subtree with a ▾/▸ disclosure toggle. Default state is
|
||||
set at line 116: `useState(true)`, i.e. expanded. Each nested
|
||||
object/array has its own independent `expanded` state.
|
||||
- `webapp/src/components/LogPreview.tsx:160-164` — `LogMessage` is the
|
||||
per-row entry point: parses the line via `tryParseJsonObject`
|
||||
(`:82-94`) and renders through `JsonValueView` when it succeeds.
|
||||
- Empty objects/arrays already short-circuit to plain `{}`/`[]` with
|
||||
no toggle (`:118-125`) — unaffected by this change.
|
||||
|
||||
## Scope
|
||||
|
||||
- Flip the default in `JsonNode` (`LogPreview.tsx:116`) from
|
||||
`useState(true)` to `useState(false)` so JSON subtrees render
|
||||
collapsed until the user clicks to expand.
|
||||
- Since expand state is per-node (not shared across the tree), this
|
||||
changes every node in a JSON log line's tree independently — confirm
|
||||
that's the intended look (top-level object collapsed with `{...}`
|
||||
rather than only nested children collapsing) rather than needing a
|
||||
"expand root, collapse children" split default.
|
||||
@@ -0,0 +1,34 @@
|
||||
# 094 — Adopt rustfmt and format the codebase
|
||||
|
||||
## Summary
|
||||
|
||||
The Rust backend has no `rustfmt.toml` and isn't currently formatted
|
||||
to rustfmt's defaults: `cargo fmt -- --check` exits non-zero, reporting
|
||||
diffs across 24 files (352 diff hunks total).
|
||||
|
||||
## Where
|
||||
|
||||
- No `rustfmt.toml` / `.rustfmt.toml` exists at the repo root.
|
||||
- `cargo fmt -- --check` reports diffs in `src/api/{dto,mcp_server,mod}.rs`,
|
||||
`src/cli/{mod,sessions}.rs`, `src/config/{mod,profile}.rs`, and most
|
||||
of `src/engine/**` (`alerts.rs`, `data_value.rs`, `filter_datetime.rs`,
|
||||
`filter_parser.rs`, `ingest.rs`, `logfmt.rs`, `message.rs`, `mod.rs`,
|
||||
`promql.rs`, `rate.rs`, `snapshot/{filter,metrics,mod,params,query,sort}.rs`,
|
||||
`tests.rs`). Heaviest: `src/engine/snapshot/mod.rs` (~65 hunks),
|
||||
`src/config/profile.rs` (~38), `src/api/mod.rs` (~35),
|
||||
`src/engine/alerts.rs` (~28).
|
||||
- `Makefile:26` (`lint-be`) currently only runs `cargo clippy
|
||||
--all-targets` — no `cargo fmt --check` step.
|
||||
|
||||
## Scope
|
||||
|
||||
- Add a `rustfmt.toml` if any defaults should be overridden (otherwise
|
||||
an empty/absent file is fine and rustfmt defaults apply — decide
|
||||
whether to keep the file empty for documentation purposes or omit
|
||||
it).
|
||||
- Run `cargo fmt` across the whole `src/` tree as a single,
|
||||
non-functional formatting commit (don't mix with logic changes, to
|
||||
keep the diff reviewable and `git blame`-friendly).
|
||||
- Add a `cargo fmt --check` step to `make lint` (or a new `make
|
||||
fmt-check` wired into CI) so future changes can't drift out of
|
||||
format again.
|
||||
@@ -0,0 +1,41 @@
|
||||
# 095 — Resolve clippy warnings and make lint enforce a clean run
|
||||
|
||||
## Summary
|
||||
|
||||
`make lint`'s backend target runs `cargo clippy --all-targets` with no
|
||||
`-D warnings` (or equivalent), so clippy warnings accumulate silently
|
||||
instead of failing the lint step. There are currently 23 unique
|
||||
warnings across 5 lint categories.
|
||||
|
||||
## Where
|
||||
|
||||
- `Makefile:26` (`lint-be:`) — `cargo clippy --all-targets`, no denial
|
||||
flag.
|
||||
- `cargo clippy --all-targets` currently reports, by lint:
|
||||
- `clippy::cloned_ref_to_slice_refs` — 10 occurrences, mostly in
|
||||
`src/engine/alerts.rs` test code (e.g. around lines 469, 472, 493,
|
||||
534).
|
||||
- `clippy::collapsible_if` — 8 occurrences, concentrated in
|
||||
`src/engine/snapshot/mod.rs` (lines 608, 940, 961) and
|
||||
`src/engine/alerts.rs` (around lines 510, 512).
|
||||
- `clippy::type_complexity` — 3 occurrences.
|
||||
- `clippy::single_match` — 1 occurrence.
|
||||
- `clippy::unnecessary_get_then_check` — 1 occurrence,
|
||||
`src/engine/tests.rs:2234`
|
||||
(`entries[1].params.get("level").is_none()`).
|
||||
|
||||
## Scope
|
||||
|
||||
- Fix all 23 current warnings (mechanical for most: `from_ref` for the
|
||||
slice-ref clone cases, collapsing nested `if`s, `is_none()` →
|
||||
`!contains_key`/`get().is_none()` equivalent simplification per
|
||||
clippy's suggestion, breaking up the complex types clippy flags or
|
||||
adding a type alias, converting the single-arm `match` to `if let`).
|
||||
- Once clean, change `Makefile:26` to deny warnings (e.g. `cargo
|
||||
clippy --all-targets -- -D warnings`) so the target fails on any
|
||||
future regression instead of just printing them.
|
||||
- Don't add blanket `#[allow(...)]`s to silence these — fix the
|
||||
underlying code per clippy's suggestion, matching the existing
|
||||
`#[allow(dead_code)]` uses elsewhere in the codebase which are for
|
||||
genuinely-unused-for-now items, not a precedent for suppressing
|
||||
active lints.
|
||||
@@ -0,0 +1,34 @@
|
||||
# 096 — Add single-character short flags to clap CLI args
|
||||
|
||||
## Summary
|
||||
|
||||
None of the CLI's clap-defined flags have a `short` form — every
|
||||
option is long-only, including ones that are typed often
|
||||
(`--file`, `--metrics-url`).
|
||||
|
||||
## Where
|
||||
|
||||
- `src/cli/mod.rs:20-39` — top-level `Args` struct, all fields
|
||||
`#[arg(long, ...)]` with no `short`:
|
||||
- `port: u16` — `:25-26`, `default_value_t = 0`
|
||||
- `settings_path: Option<PathBuf>` — `:28-29`
|
||||
- `file: Option<PathBuf>` — `:31-32`
|
||||
- `metrics_url: Option<String>` — `:34-35`
|
||||
- `name: Option<String>` — `:37-38`
|
||||
- `src/cli/mod.rs:41-50` — `Command::Mcp` subcommand, also long-only:
|
||||
- `session: Option<String>` — `:44-45`
|
||||
- `port: Option<u16>` — `:46-47`
|
||||
|
||||
## Scope
|
||||
|
||||
- Add `short` to each flag where the initial letter is unambiguous
|
||||
within its own `Args`/subcommand struct: `-p` (port), `-f` (file),
|
||||
`-m` (metrics-url), `-n` (name), `-s` (settings-path or session,
|
||||
whichever reads more naturally per subcommand — they're in separate
|
||||
structs so both can use `-s` without conflict).
|
||||
- `Mcp`'s `port` can reuse `-p` since it's a distinct struct from the
|
||||
top-level `Args`.
|
||||
- Verify with `cargo run -- --help` (and the `mcp` subcommand's
|
||||
`--help`) that clap doesn't reject any of these for collision, and
|
||||
update `README.md`'s CLI usage section if it documents flags
|
||||
explicitly.
|
||||
@@ -0,0 +1,50 @@
|
||||
# 097 — Metrics have no gauge/counter distinction, so `rate()`/`sum()`/`avg()` can be applied without a semantics guard
|
||||
|
||||
## Summary
|
||||
|
||||
`MetricPrimitiveKind` only distinguishes data *type*
|
||||
(`String`/`Number`/`Timestamp`/`Duration`/`Boolean`), not metric
|
||||
*semantics* (counter vs. gauge). PromQL's `gauge()` op is a bespoke
|
||||
query-time "take the last point" function rather than something tied
|
||||
to how a metric was declared, and nothing stops calling `rate()` on a
|
||||
metric that's conceptually a gauge, or `sum()`/`avg()` on a
|
||||
monotonically-increasing counter without rating it first.
|
||||
|
||||
## Where
|
||||
|
||||
- `src/engine/data_value.rs:70-76` — `MetricPrimitiveKind`:
|
||||
`String, Number, Timestamp, Duration, Boolean`. No `Counter`/`Gauge`
|
||||
variant.
|
||||
- `src/engine/metric_primitive.rs:8-14` — `MetricPrimitive` mirrors
|
||||
the same type-only distinction (`String, Number(Number), Timestamp,
|
||||
Duration(i64), Boolean`).
|
||||
- `src/engine/promql.rs:9-16` — `AggOp` includes `Gauge` as one of six
|
||||
query-time aggregation ops (`Sum, Avg, Count, Rate, Histogram,
|
||||
Gauge`), evaluated in `src/engine/snapshot/mod.rs:993-1001` by
|
||||
taking only `points.last()` per series — a query-side convenience,
|
||||
not a property of the declared metric.
|
||||
- The only type guard on aggregation today is
|
||||
`src/engine/snapshot/mod.rs:888-896`, which rejects `sum`/`avg` on
|
||||
`MetricPrimitiveKind::String` — there's no equivalent guard for
|
||||
applying `rate()` to a non-monotonic value or `sum()`/`avg()` to an
|
||||
un-rated counter.
|
||||
|
||||
## Scope
|
||||
|
||||
- Add a semantics field to metric declarations (e.g. extend
|
||||
`MetricPrimitiveKind` with `Counter`/`Gauge` variants, or add a
|
||||
separate `MetricSemantics` enum alongside the existing type field —
|
||||
decide which based on whether type and semantics should ever vary
|
||||
independently for the same declaration).
|
||||
- Thread that through `DeclareMetricRequest`/`MetricDeclarationRecord`/
|
||||
`MetricDeclarationDto` (the same three-places pattern noted in this
|
||||
repo's `CLAUDE.md` for declaration features: `engine.rs` message +
|
||||
handler, `settings.rs`/`profile.rs` persistence, `api.rs` route,
|
||||
plus a webapp UI affordance in `Params.tsx`/`Metrics`'s declaration
|
||||
form).
|
||||
- Use it to validate PromQL queries at evaluation time (e.g. warn or
|
||||
reject `rate()` on a declared gauge, or `sum()`/`avg()` directly on a
|
||||
declared counter without an inner `rate()`), and consider whether
|
||||
the current ad hoc `gauge()` PromQL wrapper becomes redundant once
|
||||
the declaration itself carries this information (a bare selector on
|
||||
a declared gauge could just work the way `gauge()` does today).
|
||||
@@ -0,0 +1,41 @@
|
||||
# 098 — No histogram_quantile()-style percentile query
|
||||
|
||||
## Summary
|
||||
|
||||
The PromQL layer's `histogram()` op buckets raw sample values by a
|
||||
fixed width and returns per-bucket counts — it's unrelated to
|
||||
Prometheus's classic histogram bucket/quantile model, and there's no
|
||||
way to ask for p50/p95/p99 out of a metric today.
|
||||
|
||||
## Where
|
||||
|
||||
- `src/engine/promql.rs:9-16` — `AggOp::Histogram` is one of the six
|
||||
supported ops.
|
||||
- `src/engine/snapshot/mod.rs:1002` onward — `Histogram` evaluation:
|
||||
buckets values by `bucket_width` into counts, grouped by
|
||||
`by_labels`. This answers "how many samples fell in each bucket",
|
||||
not "what value is the Nth percentile".
|
||||
- No `histogram_quantile()` function or equivalent exists anywhere in
|
||||
`promql.rs`'s parser (`parse_query`, `:261-324` recognizes only
|
||||
`sum`, `avg`, `count`, `rate`, `histogram`, `gauge`).
|
||||
- Depends on ticket 097 (gauge/counter semantics) for the broader
|
||||
question of whether a metric needs a declared "this holds raw
|
||||
observations for quantile purposes" flag, but quantile computation
|
||||
itself doesn't strictly require that — it can operate on whatever
|
||||
raw values are already stored in a metric's buckets.
|
||||
|
||||
## Scope
|
||||
|
||||
- Decide the quantile computation model: interpolate over the raw
|
||||
stored sample values in a bucket/window (simplest, exact for the
|
||||
data actually retained) vs. building a genuine bucketed-histogram
|
||||
representation with configurable bucket boundaries (closer to
|
||||
Prometheus's classic histograms, more work, needed only if retained
|
||||
raw values are later dropped in favor of pre-aggregated buckets).
|
||||
- Add a `histogram_quantile(p, metric[range])`-shaped PromQL construct
|
||||
(or extend `histogram()` with a quantile argument) to
|
||||
`promql.rs`'s parser and `AggOp`, plus an evaluator in
|
||||
`snapshot/mod.rs` alongside the existing `Histogram` handling.
|
||||
- Add dashboard/panel UI support for selecting a quantile once the
|
||||
query-language side exists (`webapp/src/components/Dashboards.tsx`
|
||||
or wherever panel query construction lives).
|
||||
@@ -0,0 +1,45 @@
|
||||
# 099 — Remove `test_handle`'s duplicate-of-`spawn` test stub
|
||||
|
||||
## Summary
|
||||
|
||||
`test_handle` is a `#[cfg(test)]` production-file function that exists
|
||||
only to give tests access to the engine's internal `mpsc::Receiver` —
|
||||
it duplicates `spawn()` almost line-for-line rather than exposing that
|
||||
receiver through `spawn()`/`EngineHandle` itself. Per this repo's
|
||||
`IDEAS.md` guidance: prefer changing a function's scope/visibility (or
|
||||
the tests) over maintaining a parallel stub.
|
||||
|
||||
## Where
|
||||
|
||||
- `src/engine/handle.rs:113-128` — `#[cfg(test)] pub(crate) fn
|
||||
test_handle(channel_capacity: usize) -> (EngineHandle,
|
||||
mpsc::Receiver<EngineMessage>)`, duplicating `spawn()`
|
||||
(`src/engine/handle.rs:103-111`) except that `spawn()` discards the
|
||||
receiver after wiring the actor loop, while `test_handle` returns it
|
||||
raw.
|
||||
- Re-exported at `src/engine/mod.rs:21`: `pub(crate) use
|
||||
handle::test_handle;`.
|
||||
- Consumed only by tests in `src/engine/ingest.rs:228` (`use
|
||||
crate::engine::test_handle;`) and called at lines 248, 264, 281, 302
|
||||
inside `#[cfg(test)] mod tests`.
|
||||
|
||||
## Scope
|
||||
|
||||
- Remove `test_handle` and instead give tests access via `spawn()`
|
||||
itself — e.g. have `spawn()` take an optional channel-capacity
|
||||
override and always hand back the receiver alongside the handle (or
|
||||
the actor join handle), with production call sites (`main.rs`)
|
||||
simply ignoring what they don't need, rather than keeping a second
|
||||
constructor whose only difference is what it returns.
|
||||
- Update the four call sites in `src/engine/ingest.rs`'s test module
|
||||
to use the adjusted `spawn()` signature.
|
||||
- Out of scope: the other test-fixture helpers in `src/engine/tests.rs`
|
||||
(`metric_request`, `declare_test_param`, `ingest_log`, `logs_query`,
|
||||
`logs_query_sorted`, etc.) are ordinary test-data builders, not
|
||||
visibility workarounds — leave those alone. Also leave the
|
||||
`#[allow(dead_code)]` items (`src/engine/message.rs:11,29`,
|
||||
`src/api/mcp_server.rs:254`, `src/config/mod.rs:27`, and similar in
|
||||
`metric_primitive.rs`/`data_value.rs`/`timestamp.rs`/`index_key.rs`)
|
||||
untouched — they weren't confirmed to be test-visibility stubs and
|
||||
need individual inspection before deciding whether they're even
|
||||
related to this ticket's pattern.
|
||||
Reference in New Issue
Block a user