[OPIK-8059] [QA] Proposed e2e specs: which rules score a playground dataset run, and rule filters - #8000
Conversation
…ilters Two specs from the QA test-radar exploration of #7989 (OPIK-8059), both verified by hand on the PR's own deployed environment first. playground-selected-metrics.spec.ts (@t2-cuj, @cap:playground.run-against-dataset) Seeds three constant-score Python rules (production/ticked, production/unticked, experiment-scope/never ticked) and runs a 3-item dataset through the Playground. Asserts the selector opens at "0 of 3 selected" — it used to default to all-ticked — and that the run is scored by exactly the ticked rule plus the experiment-scoped one, by equality on the whole score set per trace and on the total chip count in the table. A second test covers the nothing-ticked default, which is where an upgrading user with a legacy null selection lands. online-evaluation-rule-filters.spec.ts (@t2-cuj, @cap:online-evaluation.rule-filters) API-level. One enabled 100% rule at trigger_scope=both with a filter that cannot match: its production trace is skipped and its experiment trace is scored, because shouldScoreTrace never consults getFilters() on the experiment path. The absence half is anchored on the rule's own log stream rather than on a timeout. core/metrics/ and the createAutomationRule / getAutomationRule / getAutomationRuleLogs / Location-header client helpers are back-ported verbatim from main, where they landed after this branch was cut, so the merge resolves as the same change. createAutomationRule additionally takes a filters list, which the second spec needs and the pinned SDK cannot express. createTraceWithSource moves to the raw REST write and gains endTime — the sampler drops every trace with a null end_time as a partial write, so a trace seeded without one is never scored. Taxonomy updated in the same change: both specs added to their areas' specs: lists, online-evaluation.rule-filters flipped to covered: true.
⏱️ pre-commit per-hook timing
⏭️ 41 skipped (no matching files changed)
|
…gger-scope' into pr-8000
Add the metric-selector data-testids the POM was working around, and stop the nothing-ticked test claiming coverage of the persisted-null migration. MetricSelector rows already key on rule.id, so the handles are cheap: trigger, per-row (by rule id) and the "<n> of <m> selected" summary. The POM now addresses all three by test id instead of by static text. A test-only hook belongs in the same PR as the spec that needs it, and the original reason for deferring -- the verification env served a prebuilt frontend -- no longer holds now the suite runs against a build from this branch. The second test's comment called itself the legacy-null upgrade path, but the spec never seeds scoresByDatasetId, so it exercises the fresh-session default instead. Corrected the claim rather than adding store-hydration machinery; that migration belongs in the metricSelection.ts unit tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Generated-test review —
|
| Base merged in | was 4 commits behind, including fix(playground): address review findings on scoring and polling, perf(playground): do not poll a trace no rule will score and fix(online-scoring): stop scoring playground runs without a dataset — all touching what these specs pin. Clean merge, no conflicts. |
| Metric-selector test ids | added to MetricSelector.tsx (trigger, per-row by rule.id, summary) and the POM moved onto them. |
| Nothing-ticked comment | corrected — it claimed the persisted-null upgrade path, which the spec never seeds. |
Execution evidence
The released images (opik-backend:2.2.35, opik-frontend:latest) predate
#7989, so they cannot exercise it — metricSelection.ts differs from main by
12/27 lines on this branch, and that diff is the "0 of N selected" assertion.
Backend and frontend were therefore built from the merged branch and the suite
run against that:
tests/playground/ + tests/online-evaluation/ 14 passed, 1 skipped (1.9m)
Full directories, not just the new specs, since playground.page.ts is shared
with 8 others — no regressions. The one skip is OpenRouter (Custom), no key
present, pre-existing.
Gates: tag_lint 0 problems / 47 specs · tsc --noEmit clean · playwright --list 3 tests in 2 files · eslint clean on the touched component.
Bot findings
| # | Finding | Verdict | Disposition |
|---|---|---|---|
| 1 | TraceJsonSection excludes boolean/null |
invalid | byte-identical to origin/main:173 — back-ported, not authored here; no caller passes either type |
| 2 | createAutomationRule uses rawFetch against the convention |
invalid | conventions.md bans deep-importing opik/rest_api/*, not the suite's own rawFetch; already the pattern on main (client.ts:985, :1018) |
| 3 | Brittle metric-selector handles | valid | fixed here — test ids added and consumed |
| 4 | Legacy-null migration untested |
valid | fixed here — the overclaiming comment corrected; store migration belongs in metricSelection.ts unit tests |
Each is answered on its own thread.
What holds up well
Tag honesty is the thing this review exists to check, and it is sound: both
@cap: keys exist, both fail if the capability breaks, and the assertions use
set equality plus a total chip count — an extra rule scoring the run fails,
which toContain would have missed. @t2-cuj is right per TESTING-TAGS.md
(LLM spend disqualifies t1). No fixed sleeps, no skips, literal tags. Cleanup is
in the shared automationRulesCleanup fixture with nothing in the test bodies,
and the PR adds no near-duplicate fixtures. The getAutomationRule read-back
before seeding is the sharpest part — filters is absent from the pinned SDK's
shape, so a silently-dropped payload would otherwise have made the test pass for
the wrong reason.
For #7989, not this PR
main's online-evaluation-sampling-rate.spec.ts greps
'the rate applies to production traces only' (lines 445, 481) — a log line this
branch deletes from OnlineScoringSampler.java:369. That spec needs updating
with #7989 or the merge to main goes red.
Marked ready for review at @andreic's explicit request. Note the standing rule
that this skill does not flip generated PRs itself — the draft flag is the human
gate — so this was a deliberate operator override, not the review clearing its
own gate.
Details
Two new Playwright specs proposed by the QA test-radar side flow, from an
exploratory pass over #7989 (OPIK-8059 fix: score playground dataset runs by
the selected rules). A human drove both flows by hand on the PR's own deployed
environment (
pr-7989.dev.comet.com, build2.2.41-7989-merge-3070) beforeeither was written; the exploration report is the source of the two candidates
below.
This was generated. It is a draft and needs review before merge.
Why it targets this PR's branch, not
mainBase is
jacques/OPIK-8059-playground-trigger-scope, deliberately.Both specs assert behaviour that only exists on #7989: that the playground's
metric selector defaults to nothing ticked, that an unticked production-scope
rule no longer scores a dataset run, and that a rule's filters are skipped
entirely on the experiment path. On
maintoday,shouldScoreTracedoes notmake those distinctions, so both specs would fail there for the right reason.
They should land with the change they cover.
Written and run against the PR head,
1a086dff4b780965d7da2a856300542e8abbc07a.The specs
1.
e2e/tests/playground/playground-selected-metrics.spec.ts—@t2-cuj @area:playground@cap:playground.run-against-datasetSeeds three constant-score Python rules on one project (production/ticked,
production/unticked, experiment-scope/never ticked), runs a 3-item dataset
through the Playground UI, and asserts which rules scored it — both server-side
on the resulting experiment traces and in the score chips the output rows render.
Two tests:
0 of 3 selected(this is the flip:it used to default to all-ticked); ticking one rule moves it to
1 of 3 selected; the run is then scored by exactly the ticked rule and theexperiment-scoped one, and by nothing else.
experiment-scoped rule only, while two enabled, full-rate production rules are
skipped. This is the upgrade path: a legacy persisted
nullselection maps to"none ticked".
Both assert by equality on the whole score set per trace and on the total
chip count across the table, not by containment — the regression this exists
to catch is an extra rule scoring the run, which a
toContainwould not see.The constant metrics return
1.0for any input, so the only variable in theresult is which rules ran.
playground-smoke.spec.tsalready covers this capability, but it stops at "anexperiment was created" — a run scored by the wrong set of rules ships green
under it.
2.
e2e/tests/online-evaluation/online-evaluation-rule-filters.spec.ts—@t2-cuj @area:online-evaluation@cap:online-evaluation.rule-filters(wascovered: false)API-level, no browser. One enabled rule at 100% and
trigger_scope: both, witha filter that cannot match. Its production (
source=sdk) trace is skipped andits experiment trace is scored 1.0 — filters are not consulted on the experiment
path at all.
The absence half is anchored on the rule's own log stream (
… does not match the configured filters), not on a timeout, and the spec asserts it is thefilter skip rather than the neighbouring sampling-rate skip. It also gates on
reading the rule back before seeding anything:
filtersis absent from thepinned SDK's evaluator shape, so a payload the backend quietly dropped would
leave an unfiltered rule behind and make the whole test pass for the wrong
reason.
Worth a reviewer's eye on the product, not the test: a rule filtered down to a
slice of production now scores every experiment trace in its project. The code
comment argues it ("an experiment run is not a stream") and
ignoresFiltersOnExperimentTracespins it, so this spec encodes it as intended —but it is a widening the PR title does not advertise.
Supporting changes
core/metrics/and thecreateAutomationRule/getAutomationRule/getAutomationRuleLogs/Location-header additions tocore/backend/client.tsare back-ported verbatim frommain, where theylanded after this branch was cut. Copied rather than rewritten so the merge
into
mainresolves as the same change. Two deliberate deviations, bothneeded here:
createAutomationRuletakes afilterslist (filtersislikewise absent from the pinned SDK's shape), and
getAutomationRulereturnsit.
createTraceWithSourcemoves to the raw REST write and gainsendTime—also
main's shape.OnlineScoringSamplerdrops every trace with a nullend_timeas a partial write, so a trace seeded without one is never scored,and a scoring spec built on it would assert nothing.
pom/playground.page.tsgains metric-selector and score-chip locators.Metric-selector test ids — added during review
MetricSelectororiginally carried nodata-testid: its trigger is a bare<div>(RadixPopoverTrigger asChild) and its rows are unlabelled<div>s, sothe POM addressed them through static text. That was flagged in review and is now
fixed in this PR — the rows already keyed on
rule.id, so the handles werecheap:
playground-metric-selector-triggeron the triggerplayground-metric-selector-row-${rule.id}on each rule rowplayground-metric-selector-summaryon the " of selected" rowThe POM addresses all three by test id. The original reason for deferring — the
verification environment served a prebuilt frontend image, so a spec depending on
a new testid could not have been run before being proposed — no longer applies:
the re-run below used a frontend built from this branch, so the test ids are
exercised rather than assumed.
Taxonomy
Updated in the same change: both specs added to their areas'
specs:lists,online-evaluation.rule-filtersflipped tocovered: true, tier: t2-cuj, andplayground.run-against-dataset's note extended to record what the new t2coverage actually pins.
rule-filterscarries a note scoping the claim to theAPI — no spec drives the filter editor in the rule dialog.
What I deliberately did not write
online-evaluation-sampling-rate.spec.ts's "A 0% rule skips every SDK tracebut still scores experiment, playground and optimization traces" test (on
main; it post-dates this branch) fails on this build, in three places:playground traces now go through the sampling rate, optimization traces are no
longer scorable at all, and the
the rate applies to production traces onlylog line it greps for is deleted. All three are intended consequences of this
diff. That spec needs updating with this PR or it turns the merge red. It
is an edit to an existing spec on an already-covered capability, so it does not
belong in a new-test PR — flagging it here instead.
free-form
source=playgroundrun and the full 8-rule × 5-source branch tableare already pinned by backend unit tests (
OnlineScoringSamplerTest) at a farlower cost than an e2e, and the score-cell polling observation is a timing
measurement, not an assertion a spec can hold stable.
Testing
Both were run against the PR's own deployment (
OPIK_BASE_URL=https://pr-7989.dev.comet.com,OPIK_DEPLOYMENT=oss, workspacedefault) before this PR was opened.All three passed, and passed again on a repeat run and at
--workers=2. Becausepom/playground.page.tsandcore/backend/client.tsare shared, the wholetests/playground/directory was re-run as well: 9 passed, no regressions.(
compare-json-key-sortingandoptimization-trial-logsreachcreateTraceWithSourceonmain; on this branch the new spec is its onlyconsumer.)
Also green:
npx tsc --noEmit, andpython3 tests_end_to_end/coverage/tag_lint.py --taxonomy tests_end_to_end/coverage/taxonomy.yaml --estate tests_end_to_end→
0 problem(s).Review re-verification (2026-08-27)
Reviewed via
/review-generated-tests. Two changes landed on this branch as aresult, plus a merge:
jacques/OPIK-8059-playground-trigger-scope, includingfix(playground): address review findings on scoring and polling,perf(playground): do not poll a trace no rule will scoreandfix(online-scoring): stop scoring playground runs without a dataset, all ofwhich touch the behaviour these specs pin. Merged clean, no conflicts.
legacy persisted-
nullupgrade path, but the spec never seedsscoresByDatasetId, so it exercises the fresh-session default. The claim nowmatches what it asserts; the store migration belongs in the
metricSelection.tsunit tests.Re-run against a stack built from this merged branch (backend + frontend from
source via
scripts/dev-runner.sh, not a prebuilt image — the released imagespredate #7989 and cannot exercise it):
The skip is
OpenRouter (Custom)inplayground-providers— noOPENROUTER_API_KEYpresent; pre-existing and unrelated. The full directorieswere run, not just the new specs, because
playground.page.tsis shared with 8other specs: no regressions.
Gates on the merged tree:
tag_lint0 problems (47 specs) ·tsc --noEmitclean·
playwright --list3 tests in 2 files ·eslintclean on the touchedcomponent.
All four
baz-reviewerfindings are answered on their threads: two invalid (oneback-ported verbatim from
main, one misreadingconventions.md, which bansdeep-importing
opik/rest_api/*rather than the suite's ownrawFetch— alreadythe pattern on
mainatclient.ts:985/:1018), two valid and fixed here.One item for #7989, not this PR:
main'sonline-evaluation-sampling-rate.spec.tsgreps
'the rate applies to production traces only'(lines 445, 481), a log linethis branch deletes from
OnlineScoringSampler.java. That spec needs updatingwith #7989 or the merge to
maingoes red.Change checklist
Test-only, plus three
data-testidattributes onMetricSelectorthat exist sothe specs can address it (no logic, styling or rendered-text change). The
"documentation" box covers the taxonomy
note:updates, which are how thisestate records what a capability's coverage actually pins.
Issues
Follow-up, not resolved here:
main'sonline-evaluation-sampling-rate.spec.tsgreps a log line this branch deletes (
OnlineScoringSampler.java:369), so itneeds updating with #7989 or the merge to
maingoes red. Tracked against thatPR rather than this one.
AI-WATERMARK
AI-WATERMARK: yes
release-test-proposal/ exploration sideflow) authored the specs;
/review-generated-testsreviewed them and appliedthe review fixes.
claude-opus-5)core/metrics/+core/backend/client.ts+pom/playground.page.tstest support, the taxonomy entries, and the threeMetricSelectortest ids.pr-7989.dev.comet.combefore either spec was written, and reviewed and directed the follow-up run
described under Testing.
Documentation
No user-facing documentation change. Coverage bookkeeping lives in
tests_end_to_end/coverage/taxonomy.yamland is updated in this PR: both specsadded to their areas'
specs:lists,online-evaluation.rule-filtersflipped tocovered: true, tier: t2-cujwith a note scoping the claim to the API, andplayground.run-against-dataset's note extended to record what the new t2coverage pins.