Pass over src/*.rs, webapp/src/**, and the in-app "?" docs prose:
condensed multi-paragraph doc comments to single lines that state the
non-obvious why, deleted comments that only restated adjacent code, and
tightened the Docs.tsx help-panel wording. No logic, structure, or code
formatting changed — verified via cargo test (143 passed) and
tsc -b && vite build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
Adds AlertRule/AlertCondition/AlertSink models with CRUD persisted on
Settings (mirroring CssEntry's shape), per-line trigger evaluation hooked
into the engine's ingest path (Immediate/Threshold/NewValue/Ratio
conditions), and best-effort ntfy delivery spawned off the ingest hot path.
Design notes:
- Alert queries reuse FilterBar syntax but only support the
regex()/sequence/timestamp subset (no metric[param] comparisons), since
resolving those would require a full bucket rescan per alert per ingested
line rather than cost proportional to new logs.
- Ratio's zero-denominator case skips evaluation for that line rather than
treating the ratio as infinite.
- NewValue's seen-value set is in-memory only and does not survive a
restart, matching the engine's other non-persisted runtime state.
engine::run now takes a SettingsHandle (moved from api.rs to settings.rs so
engine.rs can depend on it without a circular import) to read the current
alert rules on each ingest.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
Adds a filter evaluator in engine.rs that compiles a parsed FilterExpr
against the current index once per query (regex compiled once,
sequence/timestamp compared directly against IngestedLog fields,
other metric comparisons resolved to a HashSet<u64> of matching
sequences via a bucket scan) and evaluates it per log line with no
further index access. Wires this into EngineQuery::Logs/Timeline via
a new LogsParams::filter/TimelineParams::filter field, and adds
EngineResponse::Error so a malformed filter string surfaces as a
query-level error (mapped to 400 in api.rs).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
Introduces a standalone src/filter_parser.rs module (no dependency on
engine/index types) that parses the FilterBar query language into a
FilterExpr AST using a pest grammar (src/filter_parser.pest). Supports
regex("...") searches, metric_name[param] <op> value comparisons,
reserved sequence/timestamp metrics, and and/or combinators with
correct precedence (and binds tighter than or) and parenthesized
sub-expressions. Evaluation against real log data is left to a later
integration ticket.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o