Currently in pipeline execution, if any null record is encountered, it
results in errors. Instead it should just ignore the null records and
continue. It is very common in pipeline vrls to use -`. = null` when
certain condition matches.
Currently if you try to self consume the histogram metrics emitted by
o2, it fails with the below message -
```
ERROR openobserve::job::promql_self_consume: error in ingesting self-metrics : missing value
```
This pr fixes this .
Reduces three independent e2e flakes. All test-infra only; no product
code.
## 1. Pipelines — `ingestData` "Premature close" (the original failure)
`LogsPage.ingestData` failed all records with `Failed to ingest N out of
N`. Server returned HTTP 200 and ingested every record, but node-fetch
2.7.0 reused a keep-alive socket the server closed during the 500ms
inter-record gap → "Premature close" reading the body (0/384 succeeded).
Fix: disable keep-alive (fresh connection per request, per-protocol
agent) + per-record retry. Verified: Pipelines shard green.
## 2. Dashboards-Core — `dashboard-chartJson.spec.js` popup flake
Custom-query tests (1 failed + 3 flaky) failed at the JSON/non-timestamp
checkbox with "popper intercepts pointer events" / detached timeouts.
Root cause (reproduced 100% locally): the field-property ODropdown
auto-closes within ~300ms when opened right after the panel's first
render — a late field-list re-render dismisses it. `toBeVisible()`
passes at t=0 but the checkbox is gone by click time. Fix:
`openFieldPropertyPopup` now opens, confirms the popup STAYS open, and
re-opens if it auto-closed (throws if it never stays open — no masking).
Verified locally across repeated runs.
## 3. Logs-Builder — `unflattened.spec.js` `_o2_id` readiness
Failed with `Failed to find _o2_id field after 3 attempts`; CI log shows
the UI detail was empty (`fields: []`) — re-ingested data wasn't
search-visible yet on the contended runner. The readiness gate existed
but its result was ignored, so the test fell through to a doomed UI scan
and burned all 4 retries (~26 min). Fix: respect the gate's result (on
timeout, wait once more and log), and widen the UI row scan 10→15. The
final assertion is unchanged — a genuinely absent `_o2_id` still throws.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
- Add logs to analyze the time taken by pipelines (can be turned on by
`ZO_PRINT_KEY_EVENT=true`)
- Add pipeline processing time to the usage reporting
- Introduce new metrics (`zo_pipeline_exec_batch_size` and
`zo_pipeline_exec_time_ms`) for time taken by pipeline.
## #12644
Dashboard panels let users pick a **unit** to format values (bytes,
seconds,
currencies, percent, etc.). This adds one new unit — **"Locale Format"**
— that
renders plain numbers with locale-aware thousand/decimal separators
using
`Intl.NumberFormat`, automatically driven by the user's selected UI
language.
Example for `1234567.89`:
| App language | Rendered |
|---|---|
| English (`en-gb`) | `1,234,567.89` |
| German (`de`) | `1.234.567,89` |
| French (`fr`) | `1 234 567,89` |
<img width="317" height="391" alt="image"
src="https://github.com/user-attachments/assets/53ca9444-0636-49d8-a92f-3a44fc79433e"
/>
## Metric chart type copy button support
<img width="599" height="267" alt="image"
src="https://github.com/user-attachments/assets/03c9c36e-ad5a-4096-bf85-8a9851fcd3c5"
/>
Design At: designs/dashboards/locale-format-unit
## Summary
Applies the same node-fetch v2 fix from PR #12799 (pipelines) to all
remaining CI jobs that show \"Premature close\" / ECONNRESET failures:
- **apiUtils.js** (`sendRequest`): adds `compress:false` +
`noKeepAliveAgent` — fixes **Streams** job
(`multiselect-stream.spec.js`, `streamname.spec.js`)
- **apiCleanup.js** (`_fetch`): same fix on the node-fetch fallback path
— fixes **Reports** job (dashboard/report API calls)
- **model-pricing.spec.js**: wraps `node-fetch` with a local safe-fetch
shim — fixes **GeneralTests** job (`/llm/models` premature close)
- **streamsPage.js**: replaces four dynamic `node-fetch` imports with a
shared `_nodeFetchSafe` helper
- **ingestionPage.js**: adds `fetchWithRetry` around native fetch to
handle transient `ECONNRESET`/premature close in `beforeEach` ingestion
**SDR fix:** `regexPatternManagement.spec.js` — `AddRegexPattern.vue`
migrated to `OForm` with submit-time Zod validation
(`AddRegexPattern.schema.ts`). The `ODrawer` primary button is no longer
disabled for empty fields; validation triggers on click and keeps the
drawer open on error. Replaces stale `expect(isDisabled).toBeTruthy()`
checks with click-then-expect-drawer-still-visible assertions.
## Root cause (same as #12799)
`node-fetch` v2 defaults to `keepAlive: true` (connection pooling) and
`compress: true` (Gunzip stream). When the server closes a pooled
connection early, the Gunzip stream throws \"Premature close\". Because
the dead socket stays in the pool, retries hit the same socket.
`keepAlive: false` + `compress: false` eliminates both failure modes.
## Test plan
- [ ] GeneralTests / `model-pricing.spec.js` — no more premature close
on `/llm/models`
- [ ] Streams / `multiselect-stream.spec.js` + `streamname.spec.js` — no
more premature close on ingestion
- [ ] Reports / `reportsScheduleNow.spec.js` — no more premature close
during beforeEach ingestion
- [ ] SDR / `regexPatternManagement.spec.js` — validation test passes
with updated assertion
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Problem
CI job **Pipelines-Regression** consistently fails on:
> `[chromium] › pipeline-regression.spec.js:73 › should validate
scheduled pipeline SQL query with metrics stream type`
with `FetchError: Invalid response body … Premature close` on every
attempt.
PR #12793 added `fetchWithRetry` to handle transient network errors, but
the underlying cause — **node-fetch v2 keep-alive pooling + gzip
decompression** — can survive retries when every connection attempt
picks up the same dead socket from the pool.
## Root cause
`node-fetch` v2 defaults:
- `compress: true` → sends `Accept-Encoding: gzip` and pipes response
through `Gunzip`
- Implicit HTTP agent with `keepAlive: true` → reuses connections across
requests
When the server closes a pooled connection before the response body is
fully read, `Gunzip` throws "Premature close." Because the bad
connection stays in the pool, retries can hit the same socket and fail
identically.
## Fix
- Use `keepAlive: false` agent → every fetch gets a fresh connection
- Set `compress: false` → bypass the `Gunzip` stream where the error
originates
- Keep the retry wrapper as a safety net for any remaining transient
faults
## Test plan
- [ ] CI: `Pipelines-Regression` job passes (especially the metrics
stream type test)
- [ ] Full `pipeline-regression.spec.js` suite passes locally
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Eliminates 1 of 3 redundant \SearchService::cache::search\ calls per
trace request by merging the Q2a (per-trace aggregate fields) SQL
directly into Q1.
### Changes
- **\uild_llm_trace_query\**: Added \extra_selects: &str\ param — Q2a
fields injected before \FROM\ for both regular and LLM trace queries
- **\get_latest_traces\**: Moved schema-dependent expression computation
before Q1 SQL building; extended Q1 with Q2a columns (span_count,
error_count, max_duration, service_count, root_service_name,
first_service_name, first_operation_name); removed entire Q2a search
block (~200 lines)
- **\process_latest_traces_stream\**: Same Q1 extension; added
early-break after 3 consecutive empty partitions for absolute time
ranges
- **\ un_q2_for_traces\**: Removed Q2a search call; populates all fields
directly from Q1 hits; keeps Q2b (service breakdown) for multi-service
traces
### Before (3 search calls per request)
1. Q1 — basic trace list
2. Q2a — \ race_id IN (...)\ for span aggregates (removed)
3. Q2b — \ race_id IN (...)\ for service breakdown
### After (2 search calls, or 1 for single-service traces)
1. Q1 — trace list with all aggregates
2. Q2b — service breakdown (multi-service only)
### Risk
Low. The removed Q2a code was entirely internal — column aliases like
\min_start_time\/\max_end_time\ had zero external references.
\TraceResponseItem\ struct unchanged. All fields now populated from Q1
with equivalent values.
---------
Co-authored-by: Giriraj Bidwai <girirajbidwai@users.noreply.github.com>
Co-authored-by: Hengfei Yang <hengfei.yang@gmail.com>
This PR fixes a UI layout bug where the pipeline graph preview popup
would overflow past the right edge of the viewport when hovering over
the View action button. By setting the side prop of OTooltip to left,
the preview card renders to the left of the button, keeping it fully
visible.
## The problem (from the #12719 review)
The pipeline's only hard gate was **"the Healer's authoritative run is
green."** That objective *rewards* making a spec pass by gutting the
assertions that test the feature. In #12719 the result was a suite that:
- dropped the fix-query-card assertion,
- asserted `count >= 0` (a tautology),
- buried real assertions inside `if`s whose `else` just logs, and
- **asserted the feature is absent** (`expectFixQueryCardNotVisible`) —
certifying a half-built feature and set to **turn red when the feature
is finished**.
> A passing test and a valuable test are different things, and we only
measured the first.
This PR makes **"done" mean "the tests would fail if the feature were
broken."**
## Deterministic backbone (no LLM) —
`.github/scripts/assert_integrity.py`
The most important guards don't depend on the model behaving:
- **Diff-aware heal guard** — snapshot the spec's assertions *before*
healing; **reject** any heal that **deleted**, **inverted**
(`toBeVisible` → `not.toBeVisible`), or **skipped** assertions to reach
green.
- **Static anti-patterns** — tautologies (`count >= 0`, `expect(true)`)
and **conditional-only** tests (every `expect` inside an `if` → the test
can pass asserting nothing).
- Conservative by design (only high-confidence patterns) — a gate with
false positives gets disabled by humans. Restored from a **trusted ref**
in verify_heal (never the PR tree's copy).
## Workflow
- New **Assertion-integrity gate** after the authoritative run + a
combined **Quality gate decision** (integrity **+** Sentinel verdict).
`pr_back` now requires `quality_status == pass` → a green-but-worthless
spec **never becomes a PR**.
- The post-heal **Sentinel re-audit now feeds the blocking gate** (was
advisory-only). LLM flakiness fails **open**; the deterministic gate is
the hard teeth.
- **`feature-incomplete`** honest exit: when a test can't pass because
the feature isn't wired, the Healer parks it `test.fixme` + writes
**evidence**; the workflow opens **no PR** and posts the gap (file:line)
on the source PR — the single highest-value output.
- `pr_back` **reconciles `test-summary.json` against the FINAL spec**
(drops phantom rows) so the PR body can't claim tests that don't exist.
## Prompts
- **Healer:** hard anti-weakening rules + the `feature-incomplete`
honest exit.
- **Sentinel:** tautology / conditional-only / negative-only-on-feature
/ name-mismatch as **CRITICAL** checks.
- **Engineer:** no dead page-object scaffolding; ban `waitForTimeout` as
a sync primitive; assertions must be real and match the test name.
- **Architect:** one spec = one area (no Frankenstein specs);
de-duplicate scenarios.
## Considered and deferred: mutation-sanity (extra feature-absent build)
Theoretically the cleanest definition of a valuable test, but **dropped
for now**: it overlaps the cheap static checks, has **no clean
"feature-absent" baseline** under merged/multi-PR input, risks false
positives that erode trust, and doubles the build. The static guards
catch the same #12719 failures at near-zero cost; prevention (Healer
can't invert + honest `feature-incomplete`) beats the expensive
detector.
## Testing
- `assert_integrity.py` unit-tested against tautology / conditional-only
(incl. single-line) / heal-removed / heal-inverted fixtures, with no
false positive on a legit `if` + unconditional `expect`.
- Workflow YAML validated; analyzer compiles.
- **First live run still needed** (per the project's "run the path
before done" rule) — to see the gate block a real weak spec and the
`feature-incomplete` comment fire.
Stacked on #12716 (ENT sister flow); the diff collapses to just the
quality program once #12716 lands.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Problem
The **Pipelines-Regression** CI job was failing on the P0 test:
> `[chromium] › RegressionSet/Pipelines/pipeline-regression.spec.js:73 ›
should validate scheduled pipeline SQL query with metrics stream type`
Failure (all 4 attempts):
```
FetchError: Invalid response body while trying to fetch
http://localhost:5080/api/default/ingest/metrics/_json: Premature close
```
The error originates in `beforeEach`, inside
`pipelinesPage.ingestMetricsData()`. In the same setup, the global log
ingestion, trace ingestion, and RUM ingestion all succeeded — only the
metrics ingestion `fetch` threw.
## Root cause
`Premature close` / `ECONNRESET` / `socket hang up` are **transient**
`node-fetch` errors that happen when the server closes a keep-alive
socket before the response body is fully read. They are not real
ingestion failures. Because the throw happens in the `beforeEach` of a
**serial** describe block, a single flaky socket error fails the whole
suite.
There was no retry on any of the ingestion `fetch` calls in
`pipelinesPage.js`.
## Fix
Add a small `fetchWithRetry()` helper and use it for the three ingestion
calls (metrics, traces, bulk). It retries (with linear backoff) **only**
on transient network errors; real HTTP failures (non-2xx) are untouched
and still throw immediately.
## Verification
Ran locally against `localhost:5080`:
- Failing P0 test in isolation — **passed** (36.9s)
- Full `pipeline-regression.spec.js` suite — **7 passed** (3.4m)
## Notes
- Only `tests/ui-testing/pages/pipelinesPages/pipelinesPage.js` is
changed.
- The transient socket flake can't be reproduced against a healthy local
server; the local runs confirm the retry wrapper is transparent on
success (no regression).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## What
Adds `NODE_OPTIONS=--max-old-space-size=8192` to all three `build-only`
scripts in `web/package.json` (default, `alpha1`, `cloud-prod`).
## Why
The web production build crashes with:
```
FATAL ERROR: Ineffective mark-compacts near heap limit
Allocation failed - JavaScript heap out of memory
```
The vite/rollup build hits Node's default ~4 GB old-space heap limit
(the GC log shows it maxing out around 4.2 GB). When the build dies,
`dist/` is never created, so the `postbuild` → `copy` step then fails
with `sh: cd: dist: No such file or directory` — a confusing symptom of
the real OOM.
Bumping the heap to 8 GB lets the build complete. This matches the
pattern already used by `test:unit:coverage`, which sets the same
option.
## Scope
Single change — only the three build script lines in `web/package.json`.
No dependency or lockfile changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Summary
The `run_tests` job's job-level `permissions: contents: read` was
overriding the top-level `issues: write` and `pull-requests: write`,
causing the "Post results comment" step to fail with `HttpError:
Resource not accessible by integration`.
## Test
- Verified on [run
#27815374013](https://github.com/openobserve/openobserve/actions/runs/27815374013)
— both matrix configurations passed all 672 queries but failed to post
results comments
- After this fix, comments will post correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Opening a `.ttv` (puffin) inverted-index file on a footer-cache **miss**
previously issued **two** sequential ranged reads against object
storage:
1. read the last 12 bytes (footer tail) to learn `payload_size`, then
2. read the payload region (`HeadMagic + JSON`).
This mirrors the one-shot tail optimization already used for
bloom-filter (`.bf`) files: pull a single fixed-size suffix that almost
always covers the whole footer region, eliminating the first round-trip.
## Why 4 KB
A `.ttv` is always a **single-segment** tantivy index, so its footer is
a small uncompressed JSON over a **fixed ~6-blob** set (`meta.json`, the
`.term/.idx/.pos/.fast` segment files, and `footer_cache`; at most ~9
even with `.store/.fieldnorm/.del`). The footer size does **not** grow
with the number of indexed fields.
Measured across 195 real `.ttv` files (4 KB – 176 MB):
| metric | min | p50 | p99 | max |
|---|---|---|---|---|
| footer payload JSON | 949 B | 1013 B | 1124 B | 1139 B |
| one-shot tail needed | 965 B | 1029 B | 1140 B | 1155 B |
| blobs / file | 6 | 6 | 7 | 7 |
So **4 KB** covers the entire footer region in one read with ~2×
headroom over the structural ceiling (~2 KB), while being far smaller
than the bloom filter's 16 KB (whose footer scales with group
cardinality, unlike here).
## Behavior
- **Common case:** 1 ranged read instead of 2 → saves ~one RTT per cold
footer open. Queries scanning many partitions save `N × RTT`.
- **Rare larger footer:** re-reads exactly the footer region, so the
worst case is no worse than before (still 2 reads).
- **Cache unchanged:** only the exact footer region (not the full 4 KB
probe) is stored in `FOOTER_DATA_CACHE`, so cache memory is identical to
today.
- **Footer-cache hit path:** untouched.
## Tests
- `cargo test -p tantivy_utils puffin` — 153 passed, incl. new
`test_slice_payload_from_suffix_roundtrip` covering both the single-read
fast path and the short-probe fallback against a real in-memory puffin
file.
- `cargo clippy -p tantivy_utils --tests` — clean.
- Verified working locally on real `.ttv` data.
1. The name is missing on score config list table
datetime error when user is clicking on arrow in quality page / any one
of the score config overview
replace hardcoded badges to obadge in all list page
when user goes to llm sessions it is taking user to traces submenu
## Summary
- Adds a matrix strategy to the query agent SQL regression suite jobs in
both `api-testing.yml` and `query-agent.yml`
- Each job now runs with `ZO_FEATURE_SINGLE_NODE_OPTIMIZE_ENABLED=false`
and `ZO_FEATURE_SINGLE_NODE_OPTIMIZE_ENABLED=true`
- Verifies both DataFusion physical optimizer paths produce identical
query results
- Matrix legs run in parallel with `fail-fast: false`
- All artifact/log names include the matrix label to avoid collisions
## Test plan
- [ ] PR CI shows two `api_query_agent` matrix legs
(single_node_opt_false and single_node_opt_true), both pass
- [ ] `/query-agent` PR comment triggers both modes and posts per-mode
results comments
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Adds a standalone workflow that runs the 672-query SQL regression suite
on-demand against a live OpenObserve instance. Triggered by
`/query-agent` PR comment or manual `workflow_dispatch`.
## How it works
```
/query-agent PR comment (or manual dispatch)
│
├── Job 1: Triage
│ - Allowlist gate (same engineering team list as council)
│ - Resolve PR number from comment or dispatch input
│ - Fetch PR diff, grep for query-engine paths
│ - Post decision comment (skip/run/dry-run)
│
└── Job 2: Run tests (if not skipped + not dry-run)
- Build OO debug binary (nightly-2026-05-20, mimalloc)
- Setup Python 3.11 + rye
- Run compute_counts.py to regenerate expected values
- Start OO server with ZO_MAX_FILE_RETENTION_TIME=1
- Run pytest tests/query_agent/ (excluding test_3_vortex.py)
- Post pass/fail comment on PR
```
## Details
- **Trigger**: Exact `/query-agent` comment on a PR (not `contains`), or
manual dispatch with `pr_number`
- **Runner**: `ubicloud-standard-4` (matches api-testing.yml)
- **Timeout**: triage 10 min, tests 60 min
- **Concurrency**: one run per PR, cancel-in-progress
- **Artifacts**: server logs + pytest logs uploaded on failure, JUnit
XML always
## Test plan
- [ ] Merge to main first (required for comment triggers to take effect)
- [ ] Comment `/query-agent` on a PR with query-engine changes →
workflow runs
- [ ] Comment `/query-agent` on a PR with docs-only changes → triage
skips
- [ ] Non-allowlist user comments `/query-agent` → no reaction
- [ ] Manual dispatch with `dry_run=true` → triage only, no tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## PR #12726 — Change Summary
### Problem
Test `should render table with histogram(_timestamp) as X and count
aggregation as Y` was flaky with:
Error: locator.scrollIntoViewIfNeeded: Element is not attached to the
DOM
at dashboard-chart.js:163
Root cause: After searchInput.fill(fieldName), Vue reactively re-renders
the field list
multiple times (debounce / virtual scroll). waitFor({ state: "visible"
}) passed during
an intermediate render, then Vue triggered another re-render, detaching
the element before
scrollIntoViewIfNeeded() could act on it.
---
### Changes Made
1. tests/ui-testing/pages/dashboardPages/dashboard-chart.js — Flakiness
Fix
- Removed redundant scrollIntoViewIfNeeded() — hover() already
auto-scrolls
- Wrapped hover() in a 3-attempt retry loop that re-waits for the
element after
each DOM detachment
- Raised initial waitFor timeout from 5000ms → 10000ms for heavier field
lists
2.
tests/ui-testing/playwright-tests/Dashboards/dashboard-table-chart.spec.js
— Two changes
a) Replaced waitForStreamComplete with page.waitForResponse + explicit
status assertion
- waitForStreamComplete silently timed out on non-200 responses — no
assertion
- page.waitForResponse resolves on any matching URL response, then
expect(apiResponse.status()).toBe(200) fails clearly on 401/500
b) Fixed vacuous assertion (Test Inspector finding)
- Original predicate had && r.status() === 200 — waitForResponse would
only resolve
on 200, making expect(apiResponse.status()).toBe(200) always true
(no-op)
- Removed status condition from predicate so the expect() is now a real
assertion
---
### Test Inspector Findings Addressed
| Finding | Status |
|----------------------------------------------------------------------|---------------------|
| Vacuous assertion — expect(apiResponse.status()).toBe(200) was no-op |
Fixed |
| Selector cross-validation — o-field-list-row-* matches
OFieldList.vue:67 | Verified |
## What changed
Adds a streaming progress bar (the same `LoadingProgress.vue` used by
dashboard panels) to the logs results table and histogram chart. The
skeleton loader in `TenstackTable.vue` now hides once any rows exist,
keeping data visible while streaming updates replace values in-place.
## Why
During `streaming_aggs`, hits arrive and replace chunk-by-chunk while
`loading` stays `true` until the stream `end` signal. The full skeleton
covered the table on every refresh, so users couldn't watch the
incremental updates. The progress bar signals an in-progress refresh
without hiding existing data.
## Summary
- The AI integrations regression test (`datasources-regression.spec.js`)
was using `.first()` to select the first integration in the sidebar, but
manifest-backed rich-content cards placed at the top of each category
render `AIRichSetupCard` or `AIIntegrationCard` — neither contains the
`[data-test="rum-content-text"]` element that the test expects.
- Fix: navigate directly to the "agno" child route and click the
agno-specific tab (`[data-test="ai-integrations-item-agno"]`). Agno is a
known stub integration without rich content that always uses the
`CopyContent` fallback.
## Test plan
- [ ] CI Playwright run: `Data Sources Regression Bug Fixes` suite
passes on chromium
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## What changed
- **FieldValuesPanel.vue** — Added `tw:relative` to the loading wrapper
so the `OInnerLoading` overlay (which is `absolute inset-0`) is
contained within the expanded section instead of spilling over the
entire field row.
- **IndexList.vue** — Reworked `cancelTraceId` to use
`cancelStreamQueryBasedOnRequestId` (HTTP stream abort) instead of the
now-removed `cancelSearchQueryBasedOnRequestId`. Updated
`cancelFilterCreator` to call `cancelTraceId`, clear `isLoading`, and
empty `traceIdMapper` entries.
- **FieldValuesPanel.spec.ts** — Regression test asserting the loading
wrapper carries `tw:relative`.
- **IndexList.spec.ts** — Un-skipped two previously skipped cancel tests
and updated them to assert on `mockCancelStreamQueryBasedOnRequestId`.
Added new regression tests for the `cancelFilterCreator` →
`cancelTraceId` flow on collapse.
## Why
The loading overlay (`OInnerLoading`) uses `absolute inset-0`
positioning. Without a positioned ancestor, it escaped to the nearest
parent (`.o-field-list__row`), covering the field row and the collapse
toggle. This made it impossible for users to collapse a slow-loading
field to cancel the request. The cancel logic itself was also broken —
it referenced a WebSocket-based cancel function that no longer matched
the actual HTTP-stream transport used for field values.