mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 16:09:05 +00:00
ccd254ad00799657409a6d64cef09b4f4fd48277
43
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
abc0fdc42d |
feat: model pricing time-based conditonal pricing (#13903)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fac24d59bb |
fix(traces): trace-log correlation fixes and Trace Graph height (#13901)
## Problem
Four defects around traces and the trace↔log correlation flows:
1. **Trace Graph is clipped.** The service map renders inside a 200px
band, vertically centred in an otherwise empty pane, instead of using
the available height.
2. **A span row's View Logs goes nowhere.** In OSS builds it navigates
to `/logs?stream=&from=NaN&to=NaN`; the logs page drops the filter and
falls back to its default window, so the user gets an unfiltered result
set instead of that span's logs.
3. **The log detail drawer has no View Trace button.** Opening the trace
for a log record only works from the inline expanded row.
4. **View Trace lived inside the JSON tab**, so it disappeared when
switching to Table.
## Root causes
**Trace Graph.** The ancestor chain was already correct — the sibling
Waterfall tab fills the same box. Only the leaf was clamped: the chart
carried `h-50! min-h-50!` (12.5rem, `!important`), which beat its own
root `h-full w-full`. That clamp dates to when the map lived in a small
collapsible timeline strip, where 200px was the intended design. When it
was promoted to a full-height tab, the scoped rule `.trace-details
.trace-chart-height { height: 12.5rem }` was left behind and its
two-class specificity silently defeated the `h-full!` added at the time.
The later design-token migration deleted the rule but preserved the
rendered result by translating it into utilities, which made the clamp
look deliberate.
**Span View Logs.** `buildQueryDetails()` reads the raw API shape
(`start_time`/`end_time`, nanoseconds), but the waterfall passes
formatted nodes, which `getFormattedSpan()` rebuilds as a fresh object
literal — camelCase, times as `startTimeUs`/`endTimeUs` in microseconds,
snake_case originals dropped. Reading `start_time` off one yields
`undefined`, hence `NaN`. The helper already had a `spanId || span_id`
fallback for the id, which is why the query string was correct while the
window was not.
**Drawer View Trace.** `JsonPreview` decides whether to offer the action
once, in `onBeforeMount`. `DetailTable` fills `rowData` in an `async
created()` — the `await` defers assignment past the child's mount even
though the work is synchronous — so the preview mounted with an empty
record, the check failed, the traces streams were never fetched, and
nothing recomputed it.
## Changes
- Trace Graph chart uses `h-full`; the orphaned `trace-chart-height`
class is dropped (its rule no longer exists) and two nested wrappers
collapse into one. Matches the full-bleed pattern in `ServiceGraph.vue`.
- The span row resolves the raw span from `spanMap` before calling the
helper — the same lookup this component already does for span data, HTTP
status and events — rather than teaching the shared helper to accept two
shapes. Guarded, since a span missing `span_id` gets a synthetic id
absent from the map.
- The span row also warns instead of navigating when no logs stream is
selected. The header's own View Logs button is disabled in that state
but the row-level one is not, so it was a silent dead end.
- `JsonPreview` re-evaluates the View Trace gate when the previewed
record changes, so the flag tracks its input rather than a single
snapshot.
- The action moves to the drawer's header row beside the wrap toggle,
reachable from every tab, with `hide-view-trace` on `JsonPreview`
(mirroring the existing `hide-view-related`). The gate, stream fetch and
filter move into `useViewTraceAction` rather than being copied into
`DetailTable` — `service_streams_enabled` is already hand-rolled in
three files and drifted. `JsonPreview` keeps exposing the same names, so
its specs are untouched.
Two latent issues surfaced while moving the action:
- `hiddenMenus` is seeded as an array and only becomes a `Set` once
`MainLayout` has read `custom_hide_menus`, so `.has()` is not always
available. Both shapes are now accepted.
- `OSelect`'s root sets `w-full`, which won a plain `w-40` and pushed
the button outside the drawer — the same class-collision shape as defect
1. Pinned with `w-40!`.
## Tests
No behaviour is asserted by new specs; existing suites cover the touched
paths and pass unmodified:
- `JsonPreview.spec.ts` — 91 passed (unchanged, including the assertions
on `tracesStreams`)
- `DetailTable.spec.ts` — 70 passed, 3 skipped
- `TraceTree.spec.ts`, `useTraces.spec.ts`,
`TraceDetailsSidebar.spec.ts` — 297 passed
- `TraceDetails` suites — 234 passed
- `SearchResult.viewTrace.spec.ts`,
`CorrelatedLogsTable.viewTrace.spec.ts` — passed
Prettier and ESLint are clean on every touched file.
## Verification
Checked against live trace and log data sharing a trace id:
- Trace Graph fills its pane (measured 549px where it previously
measured 200px), survives a tab round-trip, and reflows on window
resize.
- A span row's View Logs lands on `/logs` with the span+trace filter and
a ±60s window whose bounds match the span's real timestamps exactly;
with no stream selected it warns and does not navigate.
- The drawer's header shows the picker and button on both the JSON and
Table tabs with no overflow, and opens the trace with the correct id and
a ±15 min window.
- Console clean across every navigation.
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||
|
|
cb1b064a5d |
fix(synthetics): support to read a run's evidence without leaving the step (#13879)
## What Makes the evidence a synthetic browser check captures readable **where you already are**, instead of one tab away. Two related changes on the run-detail view: **1. An evidence row opens to its full record.** The table renders three fields the probe already captures and nothing displayed: the stack trace on a page error, the resource type, and the first-party flag. Plus the full URL — the row strips the host and then truncates, so a login URL died mid-query-string with no way to see the rest. **2. The step expansion's "Page activity" card now hosts that same table.** It used to show five severity-ranked rows and a `5 of 136` count — a card whose headline metric was its own incompleteness. It now renders the step's whole bucket with the same header, sorting, row expansion, filters, wrap and paging the Evidence tab has. Supporting work: the filter row is extracted into one component shared by both surfaces, so they cannot drift onto different controls; and the Evidence tab's step scope is a select you can change rather than a banner you can only dismiss. ## Why Every question past "was there anything?" cost a tab switch, and the correlation you arrived with — this step, this failure — had to be rebuilt by hand against a step filter. ## Notable fixes found along the way - **The Step column was sortable but sorted nothing.** It had no accessor, so the comparator read a field the row does not have, compared `undefined` to `undefined`, and left every row in place while the header showed a working arrow. - **The step expansion's content column had no width clamp.** As a flex item it kept its content's intrinsic width, so a long URL pushed it past the container and every card in it overflowed — and `truncate` and `overflow-x-auto` both went inert, since each needs a bounded parent. Measured at 1487px inside a 1000px container. - **Expand chevrons did not line up.** The row status rail's class also insets the first cell, so rows without a rail sat half a rem left. Benign rows now reserve the slot transparently. - **"Took" was never right-aligned** — the class sat on an inline span, where it aligns nothing. ## Testing `components/synthetics/`, `views/synthetics/`, `composables/synthetics/`, `utils/synthetics/` — **1398 passing**. `type-check`, `lint:design:strict` and `format:check` all clean. New behavioural coverage where it was thin: sorting is driven through the header and asserted on rendered row order rather than on column config, which is what let the non-functional Step sort ship. ## Reviewer notes - `EvidenceEvents.vue` is shared by both surfaces. The only differences left are the Step column and the page size — everything else is deliberately identical. - The step select is gated on being passed options, which is how it stays off the step card, where the step is already the scope. A test on each side pins that. - The per-step card's five-row cap is deliberately removed; pagination bounds the height instead, which bounds the card *and* keeps every event reachable in place. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
0f4b606e8e |
feat(synthetics): super-cluster support, then move synthetics to OSS (#13795)
> ### ⚠️ This PR carries four changes — review alongside openobserve/o2-enterprise#2420 > > 1. **Super-cluster support (consumer half)** — `8ceb041c43 … 5885c57b3a`. > 2. **Synthetics OSS migration** — `3a380087a3 … 2596300dbf`, nine commits — one per build-order step, plus a ninth for fixes found by running the binary. Moves synthetics out of `o2_enterprise` into `src/synthetics` (`openobserve-synthetics`), leaving only the private-VPC-agent path enterprise: `job_api::lease`, `agent/*`, private locations and the agent-setup install flow. Paired with openobserve/o2-enterprise#2420. > > 3. **Last-check columns read from the results stream** — `6af411e808`, `e7a3f2c759`. Outside the region whose scheduler runs a check, the list showed a stale status and a blank Last check / response time: `last_check_status` replicates only on change, `last_triggered_at` is a runtime column that does not replicate, and `last_response_ms` was never populated anywhere. The `synthetics_results` stream already carries all three and is federated, so the list API reads it instead of replicating the columns — no queue traffic, no schema change. Best-effort: any miss keeps the stored DB values, so search being unavailable cannot take the list down. OSS-only; no enterprise counterpart. > > 4. **Check secrets are readable in every region** — `d47a6b2345` (+ o2-enterprise `da71d7ec`). The per-org DEK is minted by each region for itself, so a check created outside the scheduler region replicated fine and then silently never ran — `resolve` could not decrypt its credentials (o2-enterprise#2451). Secrets now cross the queue **in clear** and the applier re-encrypts them under its own DEK on arrival, fixing the class at the replication boundary with no key to distribute or keep in sync. > > ⚠️ **This puts customer secrets in plaintext on the inter-region bus** — a deliberate, recorded trade-off (`o2oi_` ingest tokens and probe tokens already travel it in clear). Securing the bus itself (TLS on the gateway links, short retention on secret-carrying subjects) is the real fix and is **not** in this PR. > > ⚠️ **Deploy consumers before producers.** The payload's bytes did not change, only the meaning of the secret strings, so an un-upgraded consumer handed the new payload would write customer passwords to disk in clear. The plaintext-carrying messages therefore use new variant names (old ones retired as `CreateEncrypted`/`UpdateEncrypted`), so an old region fails serde and redelivers until upgraded instead of silently persisting plaintext. > > An OSS build can now create, schedule, dispatch to Lambda, take `resolve`/`ack` and report a check. 7 393 lines moved into the new crate; 679 lines stay enterprise. > > **🛑 Deployment prerequisite (2 of 2).** `ZO_SYNTHETICS_ENCRYPTION_KEY` must be set to the **same** base64 64-byte value in every region of a super cluster. Without it synthetics falls back to the per-org DEK and checks carrying secrets silently fail to run outside the scheduler region. It is read once at startup and pinned — changing it later requires a restart, and changing it at all orphans every secret already encrypted under the old value. > > **🛑 Deployment prerequisite (1 of 2).** 14 `O2_SYNTHETICS_*` env vars are hard-renamed to `ZO_SYNTHETICS_*` with **no fallback read**, and `ZO_SYNTHETICS_ENABLED` defaults to `false`. A deployment still setting the old names does not lose tuning — **synthetics stops running entirely, and the first symptom is checks silently not firing.** Update env vars *before* rolling the binary. > > #### Things a reviewer should look at first > > - **`super_cluster_queue` does not depend on `openobserve-synthetics`** (`cargo tree` → 0), which is what keeps the applier's loop-prevention guarantee a property of the crate graph rather than a convention. `a7d5fdd969` adds the assertion that fires if that ever changes. > - **`job_upload` was skipping authorization in OSS builds** (`4c3a0b8f0d`). Unreachable before, because no synthetics route was registered in OSS; registering it would have made it an unauthenticated artifact upload. `authorize_probe` is ungated and the bypass is gone. > - **The dependency edge had to reverse in a single commit** (`3247d4093f`). Cargo rejects an `o2_enterprise` ↔ OSS package cycle at the manifest level — `optional` and feature gating do not help — so the two edges could never coexist, and everything needed for the reversal lands together. This is why the spec's steps 4 and 5 are one commit and the config moved earlier than planned. > > #### Accepted OSS limitations, all documented in the commits > > - No notifications: `notify_check_result` ends in `alerts::alert::dispatch_notification`, which is enterprise. An OSS build runs the check and records the run; it has nowhere to send an alert, exactly as for alerts themselves. > - No per-resource RBAC on synthetics, and no OpenFGA ownership tuples — both are enterprise capabilities across the platform. > - Private locations are refused with a message rather than created. > - Agent-served public `net-*` pools are unavailable, so an OSS user needs a Lambda for every public pool (spec §1). --- Consumer half of synthetics super-cluster support. **Depends on openobserve/o2-enterprise#2420** (producer, message types, queue struct) — and **this side must deploy first**: `MessageType` bytes 83–85 hit a panicking `unreachable!()` in `From<u8>` on a consumer that does not know them, which takes down the subscriber task rather than failing one message. Based on `fix/lease-claimed-by-2270` (#13785), not `main`, so the diff is only this work. ## What this fixes Synthetics writes are not querier routes, so a create lands on ingesters in whichever region received the request and commits to that region's Postgres. There is no synthetics topic on the super-cluster queue, so a check created in a region that runs no scheduler is never seen by the region that does: the UI shows it enabled, the scheduler has never heard of it, and nothing reports the gap. ## Commits | commit | what | | --- | --- | | `8ceb041c43` | orphan-detection query + metrics | | `1c3e10da48` | apply replicated check config | | `79e6abc052` | apply replicated locations and probe tokens | | `16d4fa0fde` | render unknown live status instead of a false pending | | `5885c57b3a` | share the job-cluster predicate | ## Design notes worth reviewing **Config columns only, and the list is not restated.** `synthetics_checks::update` rebuilds its `ActiveModel` from the row already in this region, and `update_mutable_fields` writes only user-editable columns — so `next_run_at` and the alerting counters keep local values for free. Hand-listing them in the consumer would duplicate a list that already exists and let the two drift. A test asserts every runtime column comes back `Unchanged`. **`put()` is deliberately unused.** Its insert branch builds from `build_active_model`, which ends in `..Default::default()` and never assigns `owner`, `tz_offset` or `next_run_at` — a replicated create through it would drop the owner and fire a scheduled-later check immediately. **`create()` gains `use_given_id`**, matching `table::alerts::create`. It generated its own uuid, which would have made every region invent a different primary key for the same check. **Consumers apply through `infra::table` only.** The publish lives one layer up in the enterprise service, and `infra` cannot reach the enterprise crate — that layering is what stops a region re-broadcasting what it just applied. `source_cluster` does not cover this: it only stops a region re-consuming its *own* message. Each consumer greps its own source to keep this true. **Runtime tables are never replicated** — `synthetics_jobs`, `synthetics_runs`, `synthetics_agents`, and the locations `down_notified_at` claim. Asserted at both producer and consumer. ## Known gaps, named not hidden - If two regions mint a token (or location) with the same name for one org before either message crosses, the UNIQUE constraint rejects the arriving row. Local row wins, logged loudly. Erroring would redeliver to dead-letter, which is worse. - Pre-existing and untouched: `write_triggers_stream`/`write_results_stream` in the enterprise reaper post to `_meta` with the *org's* ingest token, which 401s, and neither checks response status. Orphan detection fixes this in its own path only. - Pre-existing: `create` never sets `tz_offset`, so a new check gets DB default 0 until its first edit. Replicates identically everywhere, so it causes no divergence. ## Spec corrections The spec (`docs/synthetics-lcl/2026-08-11-p1-3-super-cluster-spec.md`, local notes) is wrong in five places, all found by reading the tree: §1's invariant evidence (see the enterprise PR), §10's never-run guard, §4's column list (and it lists six runtime columns — there are seven, `alerting` is missing), §3's "private rows read-only when remote", and §7's claim that non-scheduler regions render OFFLINE (they render `pending`). ## Verification infra 42, super_cluster_queue 32, jobs 21, web 111/111 vitest + type-check + design lint, `cargo fmt --check` and clippy clean, full `--features enterprise` build. Pre-existing failures on this branch, unrelated and untouched: `super_cluster_queue::schemas::tests::test_parse_key_{valid,metrics}`, `infra::table::ratelimit::tests::test_add_batch_missing_rule_id`, and 8 web vitest failures in `buildPayload.spec.ts`/`payloadContract.spec.ts`. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d6c726c222 |
fix(synthetics): show where recorded steps will land (#13860)
## What Shows where recorded steps will land in a browser journey — as a labelled divider above the target row, previewed on hover and held for the whole recording session. ## Why Each step row carries a Record control meaning "restore the journey up to here, then record new steps before this row". It was the most expensive click in the editor — it opens an incognito window and replays every preceding step — and nothing said where the result would land until after that had already started. The only signal was a "Recording here" badge rendered inline in the anchored row's details cell, between the action icon and the action label. Sitting inside the row, it read as *this row is recording* when it means *new steps go above this row*. ## What changed - The badge is replaced by a divider on the row's top edge: two rule segments with a centred label between them, straddling the boundary so it names a position **between** rows rather than a property of one. - Hovering or focusing a row's Record control draws the same marker in a lighter tone, so the destination is legible while the click is still free. - The preview is suppressed wherever the control is disabled — the first row, a locked table, or an extension too old to restore. A preview of an action you cannot take is worse than no preview. - Focus is bound alongside hover, so the affordance is not mouse-only. Two edge cases surfaced while testing and are fixed here: - A replay started from the toolbar locks the table while the pointer rests on a row control. Nothing moves, so no `mouseleave` arrives, and the preview sat on a locked table offering a click that was now refused. - Both render modes share the details cell slot, so an anchor id reaching results mode drew a marker on a finished run — over a cell with no positioning to hold it. ## Implementation notes No `OTable` change. The marker is absolutely positioned against the details `<td>`, which becomes a containing block via the column's `meta.cellClass`. Two consequences worth knowing: - **Nothing reflows.** Toggling the marker on hover repaints only. A rule that took layout would jitter the whole table as the pointer crossed the action column. - **The gap is real, not painted.** The label sits between two `flex-1` segments rather than over a continuous rule. That centres it without measuring anything, and means it needs no background to avoid being struck through. `getCellStyle` lifts the cell's `overflow: hidden` on the marked row only, so the label can overhang the boundary. Step-name truncation is unaffected — that ellipsis lives on the inner wrapper, not the `<td>`. ## Testing - `JourneySteps.spec.ts`: 43 tests (18 new), covering the committed marker, the hover and focus preview, suppression on every disabled path, tone precedence when a committed anchor and a hover coexist, anchor reassignment, the positioning contract, and results mode. - Full journey suite green: 336 passed, 1 skipped, `BrowserJourney.spec.ts` unmodified — which is what shows the marker rides the existing `anchorId` prop rather than duplicating state. - Guards were mutation-checked: deleting each one turns the suite red, so none of them pass vacuously. - `lint`, `type-check`, `format:check` and `check-design-consistency.mjs` all clean. ## Not covered jsdom has no layout, so positioning is pinned by class assertions only — this has not been verified visually. Worth an eye on the label at the dense row height, in both themes. Out of scope, and unchanged by this PR: - The marker does not survive into recording — `BrowserJourney` replaces the whole journey table with a capture-only table while recording, so there is no row to mark. - The toolbar Record button draws no marker below the last row. - Downstream steps keep stale pass ticks after a splice, and the passed banner recomputes its total from the new step count. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
8a46a836c1 |
fix(ci): make AI review process exit reliably after posting (#13886)
## Problem A **successful** AI review shows up as a **red 25-minute CI timeout**. Two plumbing bugs, both proven from run logs: **1. Trace-export hang** (OSS #13875) ``` 10:19:53 AI Code Review complete ← review posted in 147s 10:19:53 Exporting 14 trace spans to OpenObserve... 10:20:53 Trace export failed: Span processor did not complete within 60000 ms Error: The operation was canceled ← job killed 25 min later ``` After the comment is posted, `TRACE.flush()` exports OTel spans. If OpenObserve is slow/unreachable, `forceFlush()` times out and throws, so `shutdown()` never runs — the `BatchSpanProcessor` timer + exporter sockets stay open and keep Node alive until the job's 25-min timeout. **2. Update-404** (OSS #13871) ``` Updating existing review comment #5336607219 Fatal error: gh api ... PATCH ... gh: Not Found (HTTP 404) ``` If the captured review comment is deleted mid-run, the PATCH 404s and fatal-errors the whole run. ## Fix (plumbing only — review logic untouched) 1. Bound `forceFlush()` **and** `shutdown()` against a 15s deadline (deadline timer `.unref()`'d), always run `shutdown()` in a `finally`, and **`process.exit()` explicitly** after flush so lingering OTel handles can't keep the process alive. 2. On a 404 from the comment PATCH, **fall back to posting a fresh comment**. ## Verified - `node --check` passes. - Runtime repro with an **unreachable trace endpoint**: the process exits cleanly (code 1) instead of hanging to the 25-min job timeout. ## Review feedback (addressed) - 🟡 **Flush deadline no longer hardcoded to 15s.** It's now `Math.max(traceExportTimeoutMs() + 5_000, 15_000)`, so the outer race sits above the configured `forceFlushTimeoutMillis` — a healthy-but-slow OpenObserve gets its full `O2_TRACE_EXPORT_TIMEOUT_MS` window (default 60s) and the precise inner timeout fires first. **Behavior note:** an unreachable endpoint now exits in ~65s at the default config instead of 15s (still well under the 25-min cap); lower `O2_TRACE_EXPORT_TIMEOUT_MS` for a faster exit. - 🔵 **Accurate comment-span attribution.** `updateReviewComment`/`postReviewComment` now return the real `{action, id}`, so the `github.pr.review_comment` span records `create` + the new comment id on a 404 fallback instead of the stale `update` + deleted id. Engine logic in this change is identical across OSS/ENT. (Note: the ENT branch has since merged the OpenObserve Code Review branding from `main`, so the full file is no longer byte-identical to OSS — only the plumbing touched here is.) No change to what the reviewer finds or decides. |
||
|
|
503a7054fb |
feat(ci): rebrand automated PR review as "OpenObserve Code Reviewer" (#13871)
## What & why Repackages our existing automated PR reviewer under a branded identity — **OpenObserve Code Reviewer** — and gives its comment a colorful, attention-grabbing format. This is **purely aesthetic + repackaging**. > [!IMPORTANT] > The review **engine is frozen**. The 5 specialist agents, coordinator judgment/rubric, risk tiering, XML finding contract, re-review PATCH-in-place logic, total-outage guard, DeepSeek/opencode wiring, read-only security model, and OTel tracing are all **unchanged**. Only *who signs the comment* and *how it looks* change. ## The two things that move **1. Identity (fail-safe).** The workflow mints a GitHub App installation token via `actions/create-github-app-token`, **gated on the `OO_REVIEWER_APP_ID` variable**. If the app isn't configured, it skips and falls back to `github.token` (posts as `github-actions[bot]`, exactly as today). So this PR is **safe to merge before the app exists** and auto-activates once the secrets are provisioned. **2. Presentation.** The coordinator now emits: - Decision as a native GitHub **callout box** — 🟢 `[!TIP]` approved · 🔵 `[!NOTE]` approved-with-comments · 🟡 `[!WARNING]` minor · 🔴 `[!CAUTION]` significant. - Rebranded heading `## 🔎 OpenObserve Code Review`. - Per-category glyphs 🔒 ⚡ 🧩 📝 📦 on each finding line. `sanitizeReviewBody` normalizes the heading (tolerates a missing emoji / legacy heading, idempotent on re-review) and the deterministic fallback renderer gets the same cosmetic treatment. ## To activate the brand (one-time, org admin) Register a GitHub App "OpenObserve Code Reviewer", install on this repo, then set: - Variable `OO_REVIEWER_APP_ID` (numeric App ID — this is the gate) - Secret `OO_REVIEWER_APP_PRIVATE_KEY` (the app's `.pem`) Remove the variable to instantly roll back to `github-actions[bot]`. ## Verification - JS parses; both workflows valid YAML. - Engine files byte-identical with the o2-enterprise counterpart PR. - 6/6 sanitizer heading cases pass (fresh, no-emoji, legacy, preamble, idempotent, trailing-words). - This PR's own review comment will preview the new format. Paired with the identical o2-enterprise PR (same branch name). |
||
|
|
351b356620 |
feat(alerts,slos): export as Terraform and OpenTofu (#13849)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
ecc1d5dbff |
fix(ui): clean up SLO detail and traces navigation (#13852)
## Summary This PR combines the SLO detail cleanup and the Traces navigation fixes into one change set. ### SLO cleanup - remove the internal definition generation value from the SLO detail title - remove the unused translation key and update generated locale files - normalize SLO health icon identifiers - avoid rewriting unchanged locale files during translation updates ### Traces navigation - add Spans as the first item in the Traces flyout - route Spans, Traces, Service Graph, and Service Catalog through /traces?tab=... - redirect legacy Service Graph and Service Catalog paths while preserving query state - support same-route menu switching and retain stream, time-range, and query context - remove the deprecated search_mode parameter from generated and legacy URLs - keep Traces as the default for tabless URLs ## Validation - focused Traces Vitest suites: 436 passed, 2 skipped - app TypeScript check - full frontend ESLint check - Prettier checks - live browser verification of Traces and Spans menu switching --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
c53a16b262 |
fix: script i18n (#13840)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
f112ac01a5 |
fix: dashboard layout improvements (#13815)
<h2 style="unicode-bidi: plaintext; color: rgb(204, 204, 204);
font-family: -apple-system, "system-ui", "Segoe UI",
Roboto, sans-serif; font-style: normal; font-variant-ligatures: normal;
font-variant-caps: normal; letter-spacing: normal; orphans: 2;
text-align: start; text-indent: 0px; text-transform: none; widows: 2;
word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color:
initial;">Dashboard layout & UX improvements</h2><p
style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em;
unicode-bidi: plaintext; color: rgb(204, 204, 204); font-family:
-apple-system, "system-ui", "Segoe UI", Roboto,
sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures:
normal; font-variant-caps: normal; font-weight: 400; letter-spacing:
normal; orphans: 2; text-align: start; text-indent: 0px; text-transform:
none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color: initial;">Polish
pass on the dashboard experience: settings layout, drag/resize grid
feedback, empty states, and per-tab panel counts.</p><h3
style="unicode-bidi: plaintext; color: rgb(204, 204, 204); font-family:
-apple-system, "system-ui", "Segoe UI", Roboto,
sans-serif; font-style: normal; font-variant-ligatures: normal;
font-variant-caps: normal; letter-spacing: normal; orphans: 2;
text-align: start; text-indent: 0px; text-transform: none; widows: 2;
word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color: initial;">✨
Highlights</h3><p style="white-space: pre-wrap; margin-top: 0.1em;
margin-bottom: 0.2em; unicode-bidi: plaintext; color: rgb(204, 204,
204); font-family: -apple-system, "system-ui", "Segoe
UI", Roboto, sans-serif; font-size: 13px; font-style: normal;
font-variant-ligatures: normal; font-variant-caps: normal; font-weight:
400; letter-spacing: normal; orphans: 2; text-align: start; text-indent:
0px; text-transform: none; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24);
text-decoration-thickness: initial; text-decoration-style: initial;
text-decoration-color: initial;"><strong>Grid feedback while
dragging/resizing panels</strong> (<a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/views/Dashboards/RenderDashboardCharts.vue"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170,
252);">RenderDashboardCharts.vue</a>)</p><ul
style="padding-inline-start: 2em; color: rgb(204, 204, 204);
font-family: -apple-system, "system-ui", "Segoe UI",
Roboto, sans-serif; font-size: 13px; font-style: normal;
font-variant-ligatures: normal; font-variant-caps: normal; font-weight:
400; letter-spacing: normal; orphans: 2; text-align: start; text-indent:
0px; text-transform: none; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; white-space: normal; background-color:
rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color: initial;"><li
style="unicode-bidi: plaintext;">Added a live alignment grid that
appears only during a drag or resize, drawn from GridStack's real cell
metrics (192 columns × row height).</li><li style="unicode-bidi:
plaintext;">The grid is<span> </span><strong>operation-aware</strong>:
lines snap to the top/left edges
while<span> </span><em>moving</em><span> </span>and the bottom/right
edges while<span> </span><em>resizing</em>, so the edge you're dragging
always lands on a line and every line is reachable (no skipped
positions).</li><li style="unicode-bidi: plaintext;">Panels are made
opaque during interaction (<code style="font-family: monospace; color:
rgb(208, 208, 208); background-color: rgb(60, 60, 60); padding: 2px 4px;
border-radius: 3px; word-break: break-word; font-size:
0.9em;">--color-surface-base</code>) so the grid only shows in the
gutters/empty cells, not through chart or table panels.</li></ul><p
style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em;
unicode-bidi: plaintext; color: rgb(204, 204, 204); font-family:
-apple-system, "system-ui", "Segoe UI", Roboto,
sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures:
normal; font-variant-caps: normal; font-weight: 400; letter-spacing:
normal; orphans: 2; text-align: start; text-indent: 0px; text-transform:
none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color:
initial;"><strong>Empty states</strong> (<a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/lib/core/EmptyState/presets.ts"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170, 252);">presets.ts</a>, <a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/components/dashboards/settings/VariableSettings.vue"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170, 252);">VariableSettings.vue</a>, <a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/views/Dashboards/ScheduledDashboards.vue"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170,
252);">ScheduledDashboards.vue</a>)</p><ul style="padding-inline-start:
2em; color: rgb(204, 204, 204); font-family: -apple-system,
"system-ui", "Segoe UI", Roboto, sans-serif;
font-size: 13px; font-style: normal; font-variant-ligatures: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal;
orphans: 2; text-align: start; text-indent: 0px; text-transform: none;
widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
white-space: normal; background-color: rgb(24, 24, 24);
text-decoration-thickness: initial; text-decoration-style: initial;
text-decoration-color: initial;"><li style="unicode-bidi:
plaintext;">Replaced the generic<span> </span><code style="font-family:
monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60);
padding: 2px 4px; border-radius: 3px; word-break: break-word; font-size:
0.9em;">NoData</code><span> </span>placeholder with<span> </span><code
style="font-family: monospace; color: rgb(208, 208, 208);
background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px;
word-break: break-word; font-size:
0.9em;">OEmptyState</code><span> </span>for the Variables settings table
and the Scheduled Dashboards list.</li><li style="unicode-bidi:
plaintext;">Added a new<span> </span><code style="font-family:
monospace; color: rgb(208, 208, 208); background-color: rgb(60, 60, 60);
padding: 2px 4px; border-radius: 3px; word-break: break-word; font-size:
0.9em;">no-variables</code><span> </span>preset (illustration + copy +
"Add variable" action); reused the existing<span> </span><code
style="font-family: monospace; color: rgb(208, 208, 208);
background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px;
word-break: break-word; font-size:
0.9em;">no-reports</code><span> </span>preset for scheduled
dashboards.</li><li style="unicode-bidi: plaintext;">Fixed the Variables
table so a long
list<span> </span><strong>scrolls</strong><span> </span>instead of
clipping, and made the Scheduled Dashboards table fill the drawer height
so the empty state centers.</li></ul><p style="white-space: pre-wrap;
margin-top: 0.1em; margin-bottom: 0.2em; unicode-bidi: plaintext; color:
rgb(204, 204, 204); font-family: -apple-system, "system-ui",
"Segoe UI", Roboto, sans-serif; font-size: 13px; font-style:
normal; font-variant-ligatures: normal; font-variant-caps: normal;
font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start;
text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; background-color: rgb(24, 24, 24);
text-decoration-thickness: initial; text-decoration-style: initial;
text-decoration-color: initial;"><strong>Dashboard settings
layout</strong> (<a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/components/dashboards/settings/GeneralSettings.vue"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170,
252);">GeneralSettings.vue</a>)</p><ul style="padding-inline-start: 2em;
color: rgb(204, 204, 204); font-family: -apple-system,
"system-ui", "Segoe UI", Roboto, sans-serif;
font-size: 13px; font-style: normal; font-variant-ligatures: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal;
orphans: 2; text-align: start; text-indent: 0px; text-transform: none;
widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
white-space: normal; background-color: rgb(24, 24, 24);
text-decoration-thickness: initial; text-decoration-style: initial;
text-decoration-color: initial;"><li style="unicode-bidi:
plaintext;">Reworked the General settings layout.</li></ul><p
style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em;
unicode-bidi: plaintext; color: rgb(204, 204, 204); font-family:
-apple-system, "system-ui", "Segoe UI", Roboto,
sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures:
normal; font-variant-caps: normal; font-weight: 400; letter-spacing:
normal; orphans: 2; text-align: start; text-indent: 0px; text-transform:
none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color:
initial;"><strong>Tab panel-count badge</strong> (<a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/components/dashboards/tabs/TabList.vue"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170, 252);">TabList.vue</a>)</p><ul
style="padding-inline-start: 2em; color: rgb(204, 204, 204);
font-family: -apple-system, "system-ui", "Segoe UI",
Roboto, sans-serif; font-size: 13px; font-style: normal;
font-variant-ligatures: normal; font-variant-caps: normal; font-weight:
400; letter-spacing: normal; orphans: 2; text-align: start; text-indent:
0px; text-transform: none; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; white-space: normal; background-color:
rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color: initial;"><li
style="unicode-bidi: plaintext;">Each dashboard tab now shows a badge
with its panel count (section headers excluded), so tab density is
visible without opening each tab.</li></ul><p style="white-space:
pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em; unicode-bidi:
plaintext; color: rgb(204, 204, 204); font-family: -apple-system,
"system-ui", "Segoe UI", Roboto, sans-serif;
font-size: 13px; font-style: normal; font-variant-ligatures: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal;
orphans: 2; text-align: start; text-indent: 0px; text-transform: none;
widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color:
initial;"><strong>Variable dependency indicator</strong> (<a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/components/dashboards/settings/VariableSettings.vue"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170,
252);">VariableSettings.vue</a>)</p><ul style="padding-inline-start:
2em; color: rgb(204, 204, 204); font-family: -apple-system,
"system-ui", "Segoe UI", Roboto, sans-serif;
font-size: 13px; font-style: normal; font-variant-ligatures: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal;
orphans: 2; text-align: start; text-indent: 0px; text-transform: none;
widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
white-space: normal; background-color: rgb(24, 24, 24);
text-decoration-thickness: initial; text-decoration-style: initial;
text-decoration-color: initial;"><li style="unicode-bidi:
plaintext;">Variables that depend on others show a "Depends on: …" chip
(tree icon + count) in the settings table.</li></ul><p
style="white-space: pre-wrap; margin-top: 0.1em; margin-bottom: 0.2em;
unicode-bidi: plaintext; color: rgb(204, 204, 204); font-family:
-apple-system, "system-ui", "Segoe UI", Roboto,
sans-serif; font-size: 13px; font-style: normal; font-variant-ligatures:
normal; font-variant-caps: normal; font-weight: 400; letter-spacing:
normal; orphans: 2; text-align: start; text-indent: 0px; text-transform:
none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
background-color: rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color:
initial;"><strong>Panel header token</strong> (<a
href="vscode-webview://0vkpvjgb486eanj04qjfqcnqmnatevjacbbbqdp3ecrs4efo5krm/web/src/lib/styles/tokens/component.css"
target="_blank" rel="noopener noreferrer" style="color: rgb(77, 170,
252); text-decoration: rgb(77, 170, 252);">component.css</a>)</p><ul
style="padding-inline-start: 2em; color: rgb(204, 204, 204);
font-family: -apple-system, "system-ui", "Segoe UI",
Roboto, sans-serif; font-size: 13px; font-style: normal;
font-variant-ligatures: normal; font-variant-caps: normal; font-weight:
400; letter-spacing: normal; orphans: 2; text-align: start; text-indent:
0px; text-transform: none; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; white-space: normal; background-color:
rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color: initial;"><li
style="unicode-bidi: plaintext;">Lightened<span> </span><code
style="font-family: monospace; color: rgb(208, 208, 208);
background-color: rgb(60, 60, 60); padding: 2px 4px; border-radius: 3px;
word-break: break-word; font-size:
0.9em;">--color-panel-bar-bg</code><span> </span>(grey-200 → grey-100)
for a softer chart-header band; the grey-300 divider still carries the
header/body edge.</li></ul><h3 style="unicode-bidi: plaintext; color:
rgb(204, 204, 204); font-family: -apple-system, "system-ui",
"Segoe UI", Roboto, sans-serif; font-style: normal;
font-variant-ligatures: normal; font-variant-caps: normal;
letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px;
text-transform: none; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; white-space: normal; background-color:
rgb(24, 24, 24); text-decoration-thickness: initial;
text-decoration-style: initial; text-decoration-color: initial;">
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
||
|
|
ddb67f3efb |
fix(synthetics): record from a specific step, and stop silently downgrading recorded actions (#13799)
Supersedes #13777, which carried the record-from-step commits alone. Closing that in favour of this, because the two changes land in the same three files (`types/synthetics.ts`, `useSyntheticsRecorder.ts`, `JourneySteps.vue`) and reviewing them apart means resolving the same conflict twice. ## Problem **A recorded hover was stored as a click.** Playwright 1.56 added `hover` to the recorder's action model, reachable from its "Choose action" picker. The extension's action mapper seeds every step with `action: 'click'` as a placeholder for its switch to overwrite, and returned that placeholder from `default:` — so any action name the switch did not know became a click the author never performed. On the usual hover target — a nav trigger, a dropdown opener — clicking it can navigate or submit. The run still reports green. There are three separate guards meant to stop exactly this, and none of them fired, because the relabel happens upstream of all of them: | Guard | Why it missed | |---|---| | `UNSUPPORTED_REPLAY_ACTIONS` (extension) | step already said `click` | | `RETIRED_ACTIONS` → `isStorableAction` | step already said `click` | | `V2_STEP_ACTIONS` (Rust validation) | `click` is valid | The same picker offers **Right click** and **Double click** as explicit choices. Both were flattened too: `click_count` was dropped by the mapper and never existed downstream, and the probe's executor called `locator.click({ timeout })` while reading neither `button` nor `click_count`. A deliberately recorded right click replayed as a left click; a double click as a single one. **Recording could only start from scratch.** Fixing step 12 of a 15-step journey meant re-recording all 15. ## Change **Record from a specific step.** Each step gets a record action that replays the prefix before it, then flips the recorder into insert mode at that point. A restore that cannot reach the requested step reports where it stopped (`prefixFailed`) rather than silently recording from the wrong place. **Every action Playwright can record is now stored and replayed as itself.** - `hover` is a first-class v2 action — mapped, stored, validated, and executed by `Locator.hover` in the probe and `Frame.hover` in the extension player. - `button` and `click_count` travel to storage, so right click and double click replay as recorded. - An action name the mapper does not know now **throws** instead of returning the `click` placeholder. This is the part that is not hover-specific: upstream added one action in nine minors and will add more, and the next one must be a deliberate decision rather than a silent downgrade. **The extension handshake reports capabilities, not just presence.** `getStatus` now returns `extVersion` and a `capabilities` array; O2 gates affordances on `hasCapability()` with a defined pre-handshake fallback, and warns when the installed build is older than the minimum. Previously the protocol carried no version at all — Chrome updates the extension independently of when O2 deploys, so the two could disagree about the wire with no way to say so. **Two dead message declarations, opposite problems.** `setSources` no longer exists in playwright-core (sources arrive alongside the actions), so its declared variant could never fire — while its `generatedCode` field is exactly what an eject-to-code feature would reach for. Deleted. `elementPicked` is the reverse: it fires on every "Pick locator" and every click in inspecting mode, and was being discarded, which made inspect mode a dead end. Now kept. ### Compatibility `button` and `click_count` are optional with defined absent-behaviour — absent means a single left click. Every journey stored today stays valid, means the same thing, and serialises byte-identically. **No schema version bump and no migration.** A test pins that rather than leaving it as an intention. `hover` is a new vocabulary entry with no default, so the probe must be able to execute one before O2 is allowed to store one — the probe change ships first. ## Implementation | File | | |---|---| | `src/config/src/meta/synthetics.rs` | `hover` joins `V2_STEP_ACTIONS`/`V2_ELEMENT_ACTIONS`; `button` and `click_count` added to `BrowserStepV2` with validation (`left\|middle\|right`, `1..=3`) | | `web/src/types/synthetics.ts` | `button`/`clickCount` on both step types; `extVersion` + `capabilities` on `RecorderStatus`; `setSources` variant deleted and `elementPicked` typed; `prefixFailed` and insert-mode fields on `recordingStarted` | | `web/src/utils/synthetics/mapRecordedStep.ts` | promotes `button`/`clickCount` from the wire, like every other stored field | | `web/src/utils/synthetics/buildV2Steps.ts` | `hover` in `ACTION_TO_V2`; crosses camelCase → snake_case for the two click fields | | `web/src/constants/synthetics.ts` | `hover` out of `RETIRED_ACTIONS`; `MIN_EXTENSION_VERSION` | | `web/src/composables/useSyntheticsRecorder.ts` | `startRecordingFrom`, `hasCapability`, `pickedSelector`, `extensionOutdated` | | `web/src/components/synthetics/journey/{BrowserJourney,JourneySteps}.vue` | per-step record action and its wiring | `click_count` is capped at 3: the recorder only ever emits 1 or 2, and an unbounded count is a way to hold a page's event loop open. The two click fields cross from camelCase to snake_case in `buildV2Step` and nowhere else — reading `step.wire` directly inside it would have worked and invented a second path into storage. ### Depends on a recorder extension build `hover`, the click fields and the capability handshake all need the matching extension build. O2 degrades honestly without it: `hasCapability()` falls back to the pre-handshake set, and an older build raises the outdated-extension warning instead of failing on a message shape it does not recognise. ## Tests - **Rust** — hover accepted as an element action; `button`/`click_count` validated at both bounds; a step carrying neither still validates (the compatibility guarantee). Two existing tests encoded "hover is rejected" and now say the opposite. 101 pass. - **Probe** — hover hovers rather than clicks; a right click sends the recorded button; a double click sends two clicks; a journey stored before these fields replays unchanged. The action union is asserted against the Rust vocabulary, so the next divergence fails a test instead of reaching an executor with no case for it. Also pinned: a selector carrying a frame hop resolves into the iframe, which had no coverage anywhere. - **Web** — 1213 across 60 files. Nine existing tests encoded "hover is retired" across constants, `buildV2Steps`, `buildPayload` and the save-gate schema; each now states the opposite deliberately. - **Extension** — the unmapped-action throw, hover round-tripping both directions, and a double click surviving the round trip as two clicks. `vue-tsc`, `cargo clippy -D warnings`, and `prettier --check` are clean. The last commit is formatting only: the three record-from-step files arrived failing `format:check`, which is a gate of its own and would not have been caught by lint. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
c246119809 |
fix(i18n): stop translated placeholder names crashing localized pages (#13833)
Fixes #13751 ## The crash vue-i18n compiles messages just-in-time (`__INTLIFY_JIT_COMPILATION__`), and a compile error is **thrown**, not warned — it tears down whatever component asked for the message. The translation pipeline had been localizing placeholder *names*, which is exactly that kind of error: ``` en-US "Are you sure you want to delete {identifier}?" zh-CN "您确定要删除 {标识符} 吗?" -> SyntaxError: 2 (INVALID_TOKEN_IN_PLACEHOLDER) ``` IAM → Service Accounts rendered blank in Chinese for this reason. The two zh-CN strings named in the issue had already been patched by hand in #13707, but the *class* was never swept. ## What was actually broken Compiling every message in every locale file with vue-i18n's own compiler found three groups: | | count | symptom | |---|---|---| | Does not compile | 132 | page blanks with `SyntaxError` — 8 locales: es, fr, it, ja, ko, nl, pt, tr | | Compiles, unknown name | 147 | `{Anzahl}` for `{count}` → value renders as an empty string | | Stale text, dropped placeholder | 5 | `{org}`, `{payer}`, `{endpoint}`, `{service}` — English source no longer has them | Worst of the first group are mangled vue-i18n literal escapes, e.g. `{'{'}team{'}'}` coming back as `{'{'} '}` — an unbalanced brace. All 279 are corrected here. The existing translated prose is kept verbatim; only the tokens are restored. The 5 stale ones are retranslated from the current English text, since restoring a token that no longer exists is not an option. ## Why it can't come back **`web/src/locales/enUsMessages.spec.ts` already did this — for en-US only.** That left the machine-translated locales, the actual source of the bug, uncovered. Renamed to `localeMessages.spec.ts` and extended to every locale, plus a second assertion that a translation only interpolates names en-US defines. That second check is a **subset** rule, not equality: dropping a placeholder the target language does not need stays legal (an English plural suffix like `{plural}` has no Japanese equivalent). Introducing a name en-US never passes a value for is always a bug. Placeholder names are collected by *running* the compiled message against a recording context rather than by pattern-matching the source, because only the compiler knows which braces are placeholders and which are literal escapes — `{'}'}` is one token whose body is the character a `{...}` regex stops at. All plural branches are evaluated. **`scripts/translations/translator.py` — `_validate` had the same regex blind spot** (`{[^{}]*}`) and never checked that the output compiles at all, which is why these landed. It now tokenizes the way vue-i18n does and rejects anything vue-i18n would refuse, so the pipeline stops emitting them rather than CI merely catching them afterwards. Escape drift (`{'{'} rows {'} rows {'}`) is caught too, because literal escapes are now part of the compared token multiset. ## Verification - The Python scanner was cross-checked against vue-i18n's compiler over **all 170,055 messages** in the 15 locale files — identical compile verdicts and identical token sets, 0 divergences. - Injecting the original `{标识符}` string back into zh-CN fails the new spec; injecting a compiling-but-wrong `{bilv}` fails the second assertion. - `npx vitest run --environment jsdom --root src/ locales/` → 45 passed. `prettier --check` and `tsc` clean on the spec. - Only `en-US.json` is imported anywhere in `web/src`, so no existing test depends on the changed locale data. ## Not in scope A handful of translations are awkward but compile and bind correctly (e.g. ko-KR renders "ingestion limit" with the eating sense of the word). Left alone to keep this diff auditable as a mechanical token restoration. |
||
|
|
0dba67d638 |
feat: composite alerts (#13797)
Design at : alerts/composite_alerts/composite_alerts.md ## Summary - add composite alert graph persistence, evaluation, scheduling, lifecycle APIs, and deletion safeguards - add composite alert UI forms, preview, references, list/detail integration, and localization - add Rust, API, Vue, and Playwright contracts ## Verification - cargo fmt --all -- --check - cargo check -p openobserve-core - cargo check -p openobserve-api-management - npm run type-check:composite-alerts - npm run type-check:app - npm run lint:ci -- --quiet - design/token/style checks - targeted Rust/Vitest suites - browser QA against local alerts UI ## Follow-ups (intentionally deferred, documented in the spec) - `RunOutcome::Error`/`NotifyFailed` outcome records on composite delivery failure (§11.5) - Incident detail live-definition resolver for composites + "definition unavailable" retention (§9.6) - `alert_composite_*` observability metrics (§16) - SLO/stream delete-conflict 409 bodies with readable-vs-hidden parent split (§10.3) - Super-cluster startup preflight (§18) <img width="1650" height="979" alt="image" src="https://github.com/user-attachments/assets/3def5246-7bf7-48fe-9a42-c0ac08271ea9" /> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
dbee572722 |
fix: slo bugs (#13784)
closes https://github.com/openobserve/o2-enterprise/issues/2397 --------- Signed-off-by: Yashodhan Joshi <yjdoc2@gmail.com> Co-authored-by: Yashodhan Joshi <yjdoc2@gmail.com> Co-authored-by: sai nikhil kethe <nikhil@openobserve.ai> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
360b989272 |
feat(slos): PromQL support for count and time-sliced SLOs, plus SLO/alert regression fixes (#13761)
Supersedes #13738, whose commit is included here as the first of four. Adds PromQL support to count and time-sliced SLOs, lets metrics streams choose SQL or PromQL, and fixes three shipped bugs found along the way. ## Shipped bug: every SQL time-slice SLO recorded wrong history `fetch_rows`' `SliQueryPlan::Single` arm normalized every hit with `with_good_column: true`, but that arm serves **two** shapes and only one projects `zo_slo_good`: | shape | projects | result | |---|---|---| | count single-query | `zo_slo_good` + `zo_slo_value` | correct | | time slice | `zo_slo_value` only | `good` defaulted to `0.0` | So every SQL time-slice slice was classified from `0.0` instead of its aggregate. With a positive threshold, `<`/`<=` (e.g. `p95 < 300ms`) recorded every slice **good** — the SLO read ~100%, the budget never burned, and no alert on it could fire. `>`/`>=` recorded every slice **bad** and fired continuously. Shipped in v0.92.0 (`4772df1723`); `to_row` had no time-slice test, which is how it got through. Fixed by deriving the flag from the SLI type in a new `single_query_rows`. Deliberately **not** fixed inside `to_row` by falling back to `total` — that would read a count bucket whose `SUM` came back NULL as *fully good*, inventing uptime. A test forbids it. **Stored history is not rewritten.** It ages out within one window; an operator can force a rebuild sooner by making a real edit (a no-op re-save does nothing — `update` bumps only on `definition_changed`). No auto-migration: bumping every time-slice SLO at boot would freeze every attached alert org-wide until backfill cleared the coverage floor, and double each org's row budget for 97 days — a self-inflicted monitoring gap to repair a reporting error. ## PromQL support **Time slice** carried a `query_language` field the planner ignored, so a PromQL time-slice SLO saved cleanly and measured nothing, forever. - `SliQueryPlan::PromQlValue` — one expression, not a good/total pair, planned at slice ends from the same helper the count arm uses. - The expression is passed through **verbatim**. Grouping comes from series labels; wrapping it in `sum by (...)` the way the SQL arm injects GROUP BY would destroy the aggregate (the sum of four pods' p95 is not a p95). - `promql_value_rows` **rejects** two series colliding on one `(slice_start, group_key)` via `RejectReason::AmbiguousSeries` instead of summing. Summing is right for counters (what `promql_rows` does) and meaningless here. Detection is unconditional, so the ungrouped case — an unaggregated `histogram_quantile` returning one series per instance — is covered. Rejects are plumbed through `fetch_rows` into `PassResult.rejected` on both the live and backfill paths. **Count** already had a working `CountSource::PromQl` backend that no UI could reach. ## Metrics streams choose SQL or PromQL `language_suits_stream` refused SQL whenever `stream_type == "metrics"`, and the form derived the language from the stream type — so metrics had no SQL option. A metrics stream is an ordinary stream with a `value` column, and `SELECT histogram(_timestamp, '5 minute'), avg(value) FROM cpu_usage` is a perfectly good SLI. SQL is now valid for any stream type; PromQL stays metrics-only. The form asks via a toggle (defaulting to PromQL) instead of deriving. ## Validation - `scope` refused for PromQL — a SQL `WHERE` fragment with nowhere to go in a PromQL plan. - Empty `query` refused for **both** languages (the SQL aggregate was previously never validated — same silent-freeze shape). - Expressions parsed with `promql-parser` at save, in `core` (`config` has no parser). - **Parse-only by design.** A grouping check requiring the root to be an aggregation whose `by(...)` matches `group_by` wrongly rejects `histogram_quantile(0.95, sum by (le, region)(...))` (function-call root; `le` is a necessary label superset) and binary-op roots like `sum by (region)(increase(a[5m])) - sum by (region)(increase(b[5m]))`. A "warn instead" variant needs a warnings channel the save API doesn't have. ## Also fixed - **Time-slice comparator 422.** `SliConfig::TimeSlice.comparator` has no serde default, so a dropdown the user never opened was omitted and the save died with a bare 422 — blocking creation of *any* time-slice SLO from the UI. Seeded to `<` only when blank, so a stored comparator survives an edit. - **SLO/alert/service-stream regressions** (the commit from #13738): fractional-threshold serde round-trip, `SloAlertCondition` UI, alerts table, service streams. ## Verification - 487 config · 206 core · 45 infra · 418 frontend — all passing; clippy `-D warnings`, `cargo fmt`, prettier, eslint, `vue-tsc` all clean. - Driven through a real browser against a running server (isolated instance, own data dir). All four SLI/language combinations create and store correctly: count·PromQL, count·**SQL over metrics**, time-slice·PromQL, time-slice·**SQL over metrics**. Also verified: the toggle swaps field shapes both ways, scope shows for SQL and hides for PromQL, PromQL-only hints appear only in PromQL mode, and editing a stored SQL-over-metrics SLO reopens as SQL with its predicate intact. - Validation rejections confirmed live with actionable messages: PromQL-with-scope, unparseable PromQL, and an empty SQL aggregate. Known limitation, pre-existing: `threshold` likewise has no serde default and is not seeded, so leaving it blank still 422s. That is genuine user error rather than an untouched-control trap; adding form validation was out of scope. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
a9dce87071 |
ci(i18n): generate translations in the PR that changes en-US.json (#13741)
The translation job ran on `push: main` and opened a separate `chore/update-translations` PR. That PR had no owner — it was never assigned to or reviewed by a human — and it left a window where main carried English strings with no translations. Move the trigger to `pull_request` and commit the generated locale files back onto the PR's own head branch, so the author who added the English strings also owns their translations and both land in one merge. Notes on the mechanics: - The commit is pushed with TRANSLATIONS_APPROVE_TOKEN, not GITHUB_TOKEN. GitHub does not trigger workflows for GITHUB_TOKEN pushes, so the new head SHA would carry no status checks and required checks could never pass — the PR would be wedged. Falls back to GITHUB_TOKEN if unset. - A guard step skips the run when the head commit is already the generated translation commit, so the PAT push cannot ping-pong. - Fork PRs are skipped: their branch lives in the contributor's repo and no token here can push to it. - concurrency flips to cancel-in-progress: true. The `false` was main-specific (merges landed faster than a run finished); on a PR branch nothing is committed until a run completes and the surviving run re-derives the pending set from .translation_state.json, so a cancel costs spend, never progress. - The push retries with a rebase in case the author pushes concurrently. Cost does not regress: re-translation across branches is prevented by the committed per-key hashes in .translation_state.json, not by the trigger, so each string is still translated once — on whichever branch first introduces it. |
||
|
|
d93d599918 |
chore: update translations from en-US.json (#13694)
Auto-generated translation updates from the English source file (`web/src/locales/languages/en-US.json`). 🤖 Generated with automated translation workflow --------- Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com> Co-authored-by: ktx-vaidehi <vaidehi.akhani@kiara.tech> |
||
|
|
3fb35f2dc1 |
ci: fix weekly npm update type-check failure and rename branch/PR (#13671)
Restrict npm-check-updates to in-major upgrades (--target minor --peer). Unrestricted major bumps pushed typescript from 5.9.x to 7.0.x, which drops the ./lib/tsc export that vue-tsc still resolves, failing the type check with ERR_PACKAGE_PATH_NOT_EXPORTED. Majors held back are now listed in the PR body so they stay visible for a deliberate manual upgrade. Rename the branch to chore/npm-update-<date> and the PR title to chore(deps): update web npm dependencies, matching the repo convention used by update-translations.yml instead of the automated/ prefix. Also fix along the way: - npm ci before checking for updates, so reported current versions are real - gate has_updates on ncu output instead of npm outdated, so the job no longer runs a full build cycle only to find nothing to commit - write scratch files to RUNNER_TEMP so an untracked outdated.json cannot make the commit step think dependencies changed - scope the change check to web/package.json and web/package-lock.json - split the commit message into separate -m flags to drop YAML indentation |
||
|
|
4772df1723 |
feat: per-group and per-series alerting, and SLO measurement (#13547)
Design at: alerts_2.md Per-group and per-series alerting, SLO measurement, and the UI for both. ## Alerts **Multi-alerts (Feature 3).** A grouped alert can opt in to evaluating and paging **per group** rather than collapsing to one verdict. Opt-in only (`multi_alert`), so no existing alert changes behaviour: the flag cannot be present in JSON written before it existed. Per-group state, transitions, silence fingerprints, disappearance/reaping and a cardinality cap, with the count-gate rules that make "most severe group" and the legacy collapsed level provably the same verdict. **Per-series alerting for PromQL.** The same feature for the metrics family, where a group is one returned **series** keyed by its full label set — that is a series' identity in Prometheus, and the expression's own `by (…)` clause is already where the label set is chosen, so a second picker could only disagree with it. One additive nullable column; NULL means off, so stored alerts are untouched. **Alert detail page**, replacing the row-click side panel: stat strip, evaluation chart with threshold marklines, per-group table and history. Simple alerts get a real history too — evaluations (one record per run) by default, level changes alongside. ## SLOs Count / time-slice / alert SLIs, error-budget and burn-rate arithmetic, rolling windows, per-group slices with an exact rollup, coverage gating, backfill, and SLO-based alerts. Two capabilities the model could not express, both added tests-first: - **Metrics-native counting** (`CountSource::PromQl`) — counter metrics have no rows for a predicate to classify, so "good" exists only as arithmetic between series, and correct counter arithmetic is `increase()`. - **Freshness semantics** (`absent_is_bad`) — for a pipeline SLO, silence IS the failure, which the default gap rule can never report. A *failed* query still freezes rather than pages; only a query that PROVED emptiness counts as bad. **SLO form**: live good/bad preview so a wrong predicate is caught before saving rather than days later, real query editors with field autocomplete, folders shared with alerts, and a move endpoint. ## Notes for review - The SLO and composite features are **deferred (TODO)**: `ZO_SLO_ENABLED` stays default **false** and the SLOs menu entry is hidden until they ship; the SLO backend and UI remain in the tree behind the flag, and composites stay pure logic + tests. - One migration (schema 61): a nullable column for the PromQL opt-in. No backfill. - Two fixes outside the feature that it surfaced: query-editor completions were duplicated per mounted editor (Monaco registers providers per *language*), and the alert detail chart had no PromQL branch, so it charted row counts against the series-count gate. - Lands together with the matching o2-enterprise PR — `QueryType::Slo` is new here and the enterprise dedup matches are exhaustive over it. - `ratelimit::test_add_batch_missing_rule_id` fails intermittently on `main` as well; unrelated to this branch. --------- Co-authored-by: Shrinath Rao <shnath@openobserve.ai> Co-authored-by: hengfeiyang <hengfei.yang@gmail.com> Co-authored-by: sai nikhil kethe <nikhil@openobserve.ai> |
||
|
|
73452af92b |
chore: update translations from en-US.json (#13425)
Auto-generated translation updates from the English source file (`web/src/locales/languages/en-US.json`). 🤖 Generated with automated translation workflow Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com> Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com> |
||
|
|
a02ab53005 |
chore: update translations from en-US.json (#13367)
Auto-generated translation updates from the English source file
(`web/src/locales/languages/en-US.json`).
🤖 Generated with automated translation workflow
Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>
|
||
|
|
be31b47172 |
chore: update translations from en-US.json (#13154)
Auto-generated translation updates from the English source file (`web/src/locales/languages/en-US.json`). 🤖 Generated with automated translation workflow Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com> Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com> |
||
|
|
4d58a1efe4 |
build: add UI code quality checks and enhance linting processe (#13174)
chore: update package.json scripts for improved type-checking fix: adjust frontmatter regex to handle BOM characters refactor: streamline error handling in dashboard data conversion style: clean up comments and formatting in various files |
||
|
|
048175094e | chore: removing glm5.2 review (#13314) | ||
|
|
8aad20b0f3 |
chore:adding deepseek also as ai-code review (#13281)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
eaba77fa34 |
fix(i18n): DeepSeek translation pipeline, BCP-47/en-US locales, +ru/pl/vi, full backfill (#13263)
## Summary
Replaces the AWS Translate i18n pipeline with a DeepSeek (LLM) backend,
standardizes locale files to BCP-47 naming with an `en-US` base, adds
new languages, and backfills **every locale to 100%**.
## What changed
### Translation engine (DeepSeek)
- Swap AWS Translate → DeepSeek via its OpenAI-compatible API
(`scripts/translations/`). Auth via the `DEEPSEEK_API_KEY` secret;
per-request 60s timeout.
- **Batched** translation (50/call) with **per-string validation**:
interpolation placeholders (`{count}`, `%s`, `@:key`), vue-i18n
**pluralization pipes** (`{n} Day \| {n} Days`), and literal escapes
(`{'@'}`, `{'{'}`, `{'\|'}`) are preserved or the string is rejected and
retried.
- Robust recovery: index-aligned batches, split-on-mismatch to isolate a
single bad string, temperature-perturbed retries, and rejection of
empty/whitespace output (never blanks a label). Removed the old
`TRANSLATION_MAX_KEYS` safety cap.
### Locale naming + base language
- All 15 locale files renamed to **BCP-47 hyphen** form (`en-US.json`,
`zh-CN.json`, `zh-TW.json`, `fr-FR.json`, …) — matching
`navigator.language` / `Intl` / HTML `lang`.
- **English base switched `en-gb` → `en-us`** (app code,
`fallbackLocale`, BCP-47 map, selector, defaults). British-Pound
currency and date-formatting `en-GB` usages left untouched. Existing
`en-gb` cookies fall back cleanly to English — no migration needed.
### New languages
- **Russian (`ru`), Polish (`pl`), Vietnamese (`vi`)** — fully generated
and wired into `localeFileMap`, `APP_LOCALE_TO_BCP47`, and the language
selector.
- **Traditional Chinese (`zh-TW`)** now maintained by the translator
alongside Simplified (`zh-CN`).
- RTL languages (`ar`, `fa`) intentionally **deferred** until the app
has `dir="rtl"` support.
### Full backfill (one-time)
- Every pre-existing locale filled from ~5.9k up to the full **8,269
keys** (~26.9k strings). `.translation_state.json` rebuilt complete, so
future runs are incremental.
### Nav i18n bug fix
- The **Dashboards** and **Data** nav group labels were hardcoded
English (bypassing i18n) and stayed English in every locale. Now
resolved via `t()` (`menu.dashboard`, new `menu.data`), localized and
re-resolving on language change.
## Verification
- Full web unit suite: **39,035 passed / 414 skipped / 0 failed** (1,167
files).
- `type-check` clean (0 TS errors).
- Locale registry sync spec green; all 15 files = 8,269 leaves.
- Translation engine verified live against `deepseek-v4-flash`.
## Notes
- Since all locales are now 100%, the post-merge `update-translations`
workflow will find nothing pending (no follow-up translation PR).
- `ar`/`fa` to be added with RTL support in a follow-up.
|
||
|
|
5ccaec3757 | fix: using opencode based ai-pr-review (#13194) | ||
|
|
3f03b77320 |
feat: Revamp metrics explorer (#13144)
Revamp the metrics explorer to enable faster visualization and exploration of metrics. Design at: metrics_cards_prd.md file --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: ktx-abhay <abhay.padamani@kiara.tech> Co-authored-by: Abhay Padamani <141122205+ktx-abhay@users.noreply.github.com> |
||
|
|
a9b67f7b8b |
chore: update translations from en.json (#13099)
Auto-generated translation updates from the English source file (`web/src/locales/languages/en.json`). 🤖 Generated with automated translation workflow Co-authored-by: oasisk <35160958+oasisk@users.noreply.github.com> Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com> |
||
|
|
56d467a29f |
chore: update translations from en.json (#13084)
Auto-generated translation updates from the English source file
(`web/src/locales/languages/en.json`).
🤖 Generated with automated translation workflow
Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>
|
||
|
|
4cdcdcc0b2 |
chore: update translations from en.json (#13075)
Auto-generated translation updates from the English source file
(`web/src/locales/languages/en.json`).
🤖 Generated with automated translation workflow
Co-authored-by: github-merge-queue[bot] <118344674+github-merge-queue[bot]@users.noreply.github.com>
|
||
|
|
2338196d35 | chore: update translations from en.json (#13040) | ||
|
|
f5ee084651 |
ci(test-assist): batch dispatcher + qa-reports backlog sweep (#12963)
Follow-up tooling to [#12946](https://github.com/openobserve/openobserve/pull/12946). Pairs with the [ENT MCP-fixes PR](https://github.com/openobserve/o2-enterprise/pull/2072) to complete the loop from "engine works on one issue" → "batch of 100 fires in parallel and lands verdicts in a reviewable table." ## Summary - **`scripts/test-assist-ci-batch.sh`** — POSIX bash script that dispatches the test-assist engine (in o2-enterprise) for a list of OSS issues. Reads issue numbers from stdin or positional args. Supports `--dry-run`, `--write-mode`, `--throttle`. Uses `gh workflow run` under the maintainer's local `gh auth`. Each dispatch is fire-and-forget; the engine's own concurrency group (`source_repo+issue`) enforces one-run-per-issue-at-a-time. - **`qa-reports/test-assist-ci-backlog-sweep.md`** — Roll-up table where verdicts land after each batch. Documents the tier structure of the priority-100 list, the verdict-to-action map (FIXED / STILL_PRESENT / INCONCLUSIVE / SKIP), and the tier queries used to build the list. Table starts empty; batches append rows. ## Usage ```bash # One-shot: dispatch for a single issue ./scripts/test-assist-ci-batch.sh 12452 # Batch: dispatch for many issues in parallel (from stdin) gh issue list --repo openobserve/openobserve --label "☢️ Bug" --label "Retested-Failed" --state open --limit 30 \ --json number --jq '.[].number' \ | ./scripts/test-assist-ci-batch.sh # Dry-run to see what would fire (no dispatches) ./scripts/test-assist-ci-batch.sh --dry-run 12452 11700 12647 # Explicitly enable label+comment writes (only after soak calibration) ./scripts/test-assist-ci-batch.sh --write-mode < /tmp/batch.txt ``` ## Interaction with the `Test-Assist-CI-Verified` label The ENT engine ([PR #2072](https://github.com/openobserve/o2-enterprise/pull/2072)) applies the `Test-Assist-CI-Verified` label to each processed issue as factual metadata (independent of `write_mode`). After a batch: - [Filter checked issues](https://github.com/openobserve/openobserve/issues?q=is%3Aissue+label%3A%22Test-Assist-CI-Verified%22) → the ones we've processed - [Filter unchecked issues](https://github.com/openobserve/openobserve/issues?q=is%3Aissue+is%3Aopen+label%3A%22%E2%98%A2%EF%B8%8F+Bug%22+-label%3A%22Test-Assist-CI-Verified%22) → the untouched backlog ## Test plan - [ ] `bash -n scripts/test-assist-ci-batch.sh` — script parses (verified locally) - [ ] `./scripts/test-assist-ci-batch.sh --dry-run 12452` — prints "DRY: would dispatch #12452" - [ ] After ENT PR #2072 merges: fire a single-issue dispatch via the script → confirm run appears in o2-enterprise Actions - [ ] Fire the 100-issue batch → confirm verdicts populate the sweep markdown 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
8af4a70be1 | fix: refactored translation job to make it more effective (#12921) | ||
|
|
e529f99100 | ci: instrument code review (#12901) | ||
|
|
53a840b5c1 | ci(ai-review): exit with code 0 instead of 1 when DEEPSEEK_API_KEY is not set (#12733) | ||
|
|
8b3b145ccb |
perf: disable multiple group by tantivy (#12528)
Refer to: https://github.com/openobserve/openobserve/pull/12348#issuecomment-4659231020 |
||
|
|
771cc39f92 | feat(search): support two-field GROUP BY via Tantivy index optimization (#12348) | ||
|
|
80984fbf6c | fix: changing layout for deepseek review comment (#12341) | ||
|
|
2cd84ff77b | fix: Load pricing from json (#10857) | ||
|
|
eff23e31b7 | fix: added unit test cases for services layer (#10867) | ||
|
|
7f55832426 |
ci: added workflow to udpate the language conversion (#9282)
added workflow to udpate the language conversion --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Bhargav <BJP232004@GMAIL.COM> |