Skip to content

[OPIK-7791] [QA] Proposed e2e specs from the 2.2.50 → 2.2.51 release exploration - #8161

Draft
CometActions wants to merge 2 commits into
mainfrom
AndreiCautisanu/OPIK-7791/online-evaluation-judge-prompt-specs
Draft

[OPIK-7791] [QA] Proposed e2e specs from the 2.2.50 → 2.2.51 release exploration#8161
CometActions wants to merge 2 commits into
mainfrom
AndreiCautisanu/OPIK-7791/online-evaluation-judge-prompt-specs

Conversation

@CometActions

Copy link
Copy Markdown
Collaborator

Generated by the release QA side flow, and it needs human review before merge.
Nothing here was written or reviewed by a person yet. It is a draft on purpose.

Where these came from

The 2.2.50 → 2.2.51 release exploration worked the release's manual-verification
list against staging.dev.comet.com/opik (workspace opik-testing). Both flows
below were verified by hand on staging first — these specs make permanent
what that pass checked once.

Both come from #8150 (OPIK-8250):
a judge message's content is persisted as a plain String whether the author
typed prose or the UI built a content array, so AutomationModelEvaluatorMapper
inferred the shape from the leading character. A prompt legitimately opening with
[ was parsed as JSON, failed, and 500'd the whole evaluators listing — every
rule in the project plus the workspace-wide findAll() the online-scoring
sampler calls per trace batch.

Which ref these were written and run against

Written and run against the release tag 2.2.51 (d5ac079), which is the
ref the exploration verified. main is simply where they have to merge, and it
will have moved on — please re-run on main before promoting this out of draft.

The specs

1. online-evaluation-json-looking-judge-prompts.spec.ts

Covers @cap:online-evaluation.list-rules (@t2-cuj).

Seeds a prose control rule plus six judge prompts that each open with [ and
reach a different branch of the mapper's fallback — [Source Text] … (the
customer prompt, not JSON at all), [1, 2] … (valid JSON, wrong element type),
[null] …, [{}] … (no type discriminator), [] …, and
[{"type": "text", "text": "example"}] Now grade the output above. Then asserts:

  • the project-scoped listing answers 200 and carries exactly the seeded
    set — the whole collection, not just containment, so a listing that silently
    dropped the unreadable rows would fail rather than pass;
  • the workspace-wide listing (the sampler-shared read) answers 200 and is
    non-empty;
  • the rules page renders a row per rule, with no 5xx from
    /automations/evaluators
    during load.

The control rule is what makes a green listing mean something: the regression was
collateral, one bad row took down every other rule in the project too.

Verification: PASSED on staging against 2.2.51.

2. online-evaluation-judge-prompt-round-trip.spec.ts

Covers @cap:online-evaluation.edit-rule (@t2-cuj), two tests.

Test 1 — an example-array-then-prose prompt survives the edit dialog and a
re-save byte for byte.
The old reader stopped at the first complete JSON array,
so [{"type": "text", "text": "example"}] Now grade the output above. lost the
instruction it exists for, and the next save wrote the truncation down. The spec
seeds over REST, asserts the API returns the string whole and not promoted to
content_array, opens the row's Actions → Edit dialog and asserts the
hydrated prompt is byte-identical, submits the dialog unchanged, reopens it
and asserts it again, then re-checks the server. The no-op submit is the gesture
that turned a read bug into permanent data loss.

Test 2 — a genuinely multimodal judge message still reads back as structured
content.
The guard against over-correcting: falling back to "it is all just a
string" would make test 1 pass while destroying every real multimodal rule. Seeds
a content_array with a text part and an image_url part, asserts the read-back
keeps url and detail, re-saves exactly what was read, and asserts it again.

Verification: both PASSED on staging against 2.2.51.

How they were run

From tests_end_to_end/e2e/, against $OPIK_BASE_URL = https://staging.dev.comet.com/opik:

WORKERS=2 npx playwright test \
  tests/online-evaluation/online-evaluation-json-looking-judge-prompts.spec.ts \
  tests/online-evaluation/online-evaluation-judge-prompt-round-trip.spec.ts \
  --reporter=list --retries=0
# 3 passed (14.4s)

Plus the estate's own three checks from .agents/skills/writing-e2e-tests:

  • tag_lint.py61 specs checked, 1 exempt, 0 problem(s).
  • Feature-directory run (I touched a shared POM) —
    npx playwright test tests/online-evaluation/ with WORKERS=2 leaves
    online-evaluation-non-object-sections.spec.ts red, in
    LogsPage.waitForReady waiting on the traces table. This is pre-existing
    staging flake, not a regression from this PR
    : the same directory run on a
    clean 2.2.51 tree with the same settings fails three specs (that one plus
    both online-evaluation-sampling-rate tests), all in the same
    LogsPage.waitForReady on the traces table. That POM is untouched here. All
    three pass when run alone.
  • npx tsc --noEmit — currently fails at the config level on 2.2.51 for
    everyone, before any file is checked:
    tsconfig.json(13,5): error TS5102: Option 'baseUrl' has been removed.
    (typescript: "^7.0.2" resolves to 7.0.2, which dropped baseUrl). Left
    alone — it is not this PR's to fix. Typechecked instead with TypeScript 5.9.3
    over the same config: the only error is the pre-existing
    core/backend/client.ts: Duplicate identifier 'deleteDashboard', present
    identically on a clean tree. This PR adds no new type errors.

I also ran a negative control on the two assertions doing the most work — the
CodeMirror prompt read and the UI row count — by mutating each to the wrong
expected value. Both failed, and the prompt read reported the full string
including the trailing instruction and the blank line. Neither assertion is
passing vacuously.

What is in the diff besides the specs

  • core/backend/client.tscreateLlmJudgeRule (the existing
    createAutomationRule only builds user_defined_metric_python rules, which
    have no content / content_array split), findAutomationRuleEvaluatorsPage
    (returns the HTTP status instead of throwing, because the status is the
    assertion), getLlmJudgeMessages, resaveAutomationRuleFromReadBack.
  • pom/online-evaluation.page.tsopenEditDialogByName, submitDialog,
    cancelDialog, readPromptMessageText, and a ruleRows getter that
    waitForReady / ruleRow now share. No selector changes.
  • coverage/taxonomy.yaml — both specs added to specs:; list-rules and
    edit-rule flipped to covered: true at t2-cuj with scoping notes.

No FE change was needed — every element already had a data-testid or a stable
role.

On the edit-rule claim: test 1 opens the dialog through the row's Edit
action, asserts what it hydrated, and submits it, so the capability is genuinely
exercised. But editing a field through the dialog — renaming, changing the
model, rewriting the prompt — is still uncovered, and the taxonomy note: says
so rather than implying more than was tested.

What I deliberately did not write

The exploration produced two candidates and both are here; nothing was
dropped.
The list was already filtered upstream — three of its five ranked
items produced no candidate:

  • Item 3 (Liquibase 000121 against clustered ClickHouse) — skipped during
    exploration: deployment-level DDL on a shared managed environment.
  • Item 4 (aiu_nano on ingested copilot spans) — blocked: cipx_spends /
    cipx_spend_blocks have DAOs and an ingestion listener but no REST read
    surface
    , so the write is triggerable and the assertion is not without direct
    ClickHouse access.
  • Item 5 (2.2.51 TS SDK packages publish) — verified against the npm
    registry, but that is release plumbing, not app behaviour; there is nothing in
    the e2e estate for it to assert.

Items 3 and 4 are the whole of #8155
and neither got a hands-on check on staging. If anything in this release still
needs a human, it is those two, and it needs a self-hosted deployment rather than
staging.

One incidental thing the exploration flagged, unrelated to this release and not
asserted on here: the online-evaluation page fires three failing background
requests on load regardless of rule content — 403 GET /v1/private/datasets/export-jobs, 404 GET /v1/private/agent-configs/blueprints/history/projects/{id}, 404 GET /v1/private/agent-insights/jobs/{id}. Spec 1's wire check is scoped to
/automations/evaluators and to 5xx precisely so it does not become a monitor
for those.

…250)

Two specs proposed by the 2.2.50 -> 2.2.51 release QA side flow, from flows a
human verified on staging first.

online-evaluation-json-looking-judge-prompts.spec.ts covers
@cap:online-evaluation.list-rules: a project holding a prose control rule plus
six judge prompts that open with '[' still lists every rule over the
project-scoped API, keeps the workspace-wide (sampler-shared) listing at 200,
and renders a row per rule with no 5xx from the evaluators endpoint.

online-evaluation-judge-prompt-round-trip.spec.ts covers
@cap:online-evaluation.edit-rule: an example-array-then-prose prompt reads back
byte-exact over the API and in the edit dialog, and survives a no-op dialog
save; a genuine content_array message still reads back structured, with its
image_url url and detail intact, after a no-op re-save.

The estate cannot reach these shapes through the create-rule dialog, which only
emits the canned templates, so both specs seed over REST. Supporting additions:
createLlmJudgeRule / findAutomationRuleEvaluatorsPage / getLlmJudgeMessages /
resaveAutomationRuleFromReadBack on the backend client, and
openEditDialogByName / readPromptMessageText / submitDialog / cancelDialog on
OnlineEvaluationPage.

Generated by the release QA side flow. Needs human review before merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added tests Including test files, or tests related like configuration. typescript *.ts *.tsx labels Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

📋 PR Linter Failed

Missing Section. The description is missing the ## Details section.


Missing Section. The description is missing the ## Change checklist section.


Missing Section. The description is missing the ## Issues section.


Missing Section. The description is missing the ## Testing section.


Missing Section. The description is missing the ## Documentation section.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

⏱️ pre-commit per-hook timing

Hook Description Result Duration
⚓ helm-docs Regenerate Helm chart README 9.10s
🐍 mypy — python sdk Static type check 1.28s
🐍 fix end of files — python sdk Ensure files end in a newline 0.03s
🐍 trim trailing whitespace — python sdk Strip trailing whitespace 0.03s
🐍 ruff-format — python sdk Format Python code (ruff) 0.01s
🐍 ruff — python sdk Lint + autofix Python (ruff) 0.01s
Total (6 ran) 10.46s
⏭️ 38 skipped (no matching files changed)
Hook Description Result
🤖 trim trailing whitespace — optimizer Strip trailing whitespace ⏭️
🤖 fix end of files — optimizer Ensure files end in a newline ⏭️
🤖 check yaml — optimizer Validate YAML syntax ⏭️
🤖 check json — optimizer Validate JSON syntax ⏭️
🤖 check toml — optimizer Validate TOML syntax ⏭️
🤖 check for added large files — optimizer Block large files (>1MB) ⏭️
🔐 detect private key — optimizer Block committed private keys ⏭️
🤖 check for merge conflicts — optimizer Block merge-conflict markers ⏭️
🤖 check for case conflicts — optimizer Block case-only name clashes ⏭️
🤖 pyupgrade — optimizer Modernize Python syntax ⏭️
🤖 ruff — optimizer Lint + autofix Python (ruff) ⏭️
🤖 ruff-format — optimizer Format Python code (ruff) ⏭️
🤖 mypy — optimizer Static type check ⏭️
📓 nbstripout — optimizer notebooks Strip notebook output ⏭️
📝 markdownlint — optimizer Lint Markdown ⏭️
🔤 codespell — optimizer Fix common misspellings ⏭️
📊 radon cc — optimizer Cyclomatic-complexity gate ⏭️
📊 radon raw — optimizer Raw size metrics gate ⏭️
📊 xenon — optimizer Fail on complexity thresholds ⏭️
📊 lizard — optimizer Cyclomatic-complexity gate ⏭️
🧹 vulture — optimizer Find dead code ⏭️
🛡️ trim trailing whitespace — guardrails Strip trailing whitespace ⏭️
🛡️ fix end of files — guardrails Ensure files end in a newline ⏭️
🛡️ ruff — guardrails Lint + autofix Python (ruff) ⏭️
🛡️ ruff-format — guardrails Format Python code (ruff) ⏭️
🛡️ mypy — guardrails Static type check ⏭️
block non-public FE plugins Block non-public FE plugins ⏭️
☕ spotless — java backend Format Java code ⏭️
🧪 pre-commit wrapper smoke tests Self-test the wrapper scripts ⏭️
🧪 rebaseline script tests Self-test the changelog re-baseline script ⏭️
🌐 eslint — frontend Lint + autofix JS/TS ⏭️
🌐 typecheck — frontend Whole-project tsc type check ⏭️
📘 eslint — typescript sdk Lint + autofix JS/TS ⏭️
📘 typecheck — typescript sdk Whole-project tsc type check ⏭️
⚙️ actionlint — github workflows Lint GitHub Actions workflows ⏭️
🐳 hadolint — dockerfiles Lint Dockerfiles ⏭️
🌈 zizmor — github workflows security Security-scan GitHub Actions workflows ⏭️
🛡️ semgrep — java backend sql Block SQL injection-prone string formatting ⏭️

…ule note

- json-looking-judge-prompts.spec.ts: the JsonLookingPrompt.why field was
  populated and never read, so a missing row failed with "row for <name>" and
  the reader had to go back to the table to learn which mapper branch that
  shape reaches. Carry name+why through the UI step and put it in the message.

- taxonomy.yaml: the edit-rule note read as though the edit dialog was driven
  for both prompt shapes. Only the plain-string half is; the content_array
  half is API-only, because the dialog renders no image part. Scoped the note
  to say so, and recorded the open question — the content_array test asserts
  content-shape preservation, which no capability in this area names, so it
  rides on edit-rule until a human decides whether to add a key or drop the tag.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@CometActions

CometActions commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

🔍 Generated-test review

PR #8161OPIK-7791 [QA] Proposed e2e specs from the 2.2.50 → 2.2.51 release exploration

CometActions · draft: yes · 6 files, 2 new specs · head 38cc35d → review commit 5fbcb78

VERDICT: ready with notes — one open decision for a human (the edit-rule
tag on the multimodal test), and one estate-wide gate failure this PR did not
cause and must not fix.

Do not flip the draft flag on the strength of this report. A person makes that
call; this review does not, and did not.


Blockers (0)

None. Both specs collect, both are placed correctly, the tags that matter are
honest, and there are no fixed sleeps, no in-body teardown, no .skip, and no
computed tags.


Should fix (1 — a decision, not an edit)

1. @cap:online-evaluation.edit-rule on a test that never opens the edit dialog
tests_end_to_end/e2e/tests/online-evaluation/online-evaluation-judge-prompt-round-trip.spec.ts:94

A genuinely multimodal judge message still reads back as structured content is
API-level throughout: create over REST, GET, PATCH the read-back verbatim,
GET again. It never opens a browser. What it actually asserts is judge-message
content-shape preservation
— that a content_array survives a read-then-write
and is not flattened into content. No capability in the online-evaluation
area names that.

Why it is not a blocker: edit-rule is also carried by the sibling test at
line 24, which genuinely drives the dialog — opens it, asserts the hydrated
prompt byte-exact, submits unchanged, re-reads. So covered: true is earned by
real UI evidence; the second tag widens the claim rather than fabricating it.

Why I did not fix it: the two honest fixes are (a) add a capability for
content-shape preservation, or (b) drop the tag — and (b) would leave the test
with zero @cap:, which the tag grammar forbids. (a) is a change to the
product's coverage denominator and belongs to you, not to this review. I did
not invent a key.
The gap is now recorded as an OPEN, needs a human call
comment above edit-rule in taxonomy.yaml so it cannot decay into a silent
widening.


Fixed in this PR (2)

Both pushed as 5fbcb78.

1. JsonLookingPrompt.why was dead data
online-evaluation-json-looking-judge-prompts.spec.ts. Six prompt shapes each
carry a why explaining which branch of AutomationModelEvaluatorMapper's
fallback they reach, and nothing ever read it: the UI row assertion failed with
row for <name>. The interface's own docstring claimed the label appears "in
every failure message", which was true of the name and false of the reason. Now
carried as {name, why} through the UI step, so a missing row reports
row for …-empty-object — valid JSON object element carrying no 'type' discriminator and names the regression on the first read.

2. The edit-rule note read as if the dialog was driven for both prompt shapes
coverage/taxonomy.yaml. It said "dialog hydration + no-op re-save … in both
the plain-string and content_array shapes". Only the plain-string half is a
dialog gesture; the content_array half is REST-only, and for a good reason the
spec states — the dialog renders no image part, so the url/detail a
regression would drop are not observable in the UI at all. The note now scopes
each half separately. This matters because the note is what the next person
reads instead of re-deriving the spec.


Notes (3)

1. The tsc gate is red on origin/main, and this PR is not the cause.

tsconfig.json(13,5): error TS5102: Option 'baseUrl' has been removed.

tests_end_to_end/e2e/package.json declares typescript: "^7.0.2" and
package-lock.json pins 7.0.2; TypeScript 7 removed baseUrl, which
tsconfig.json:13 still sets. None of tsconfig.json, package.json or
package-lock.json appears in this PR's diff, and TS5102 is raised against the
config before any source file is read.

Verified, not inferred: I checked origin/main out into a separate worktree
against the same node_modules and ran the gate — it fails identically there.

I deliberately did not fix it, and removing baseUrl is not the fix.
Stripping that one line locally turns 1 error into 94: @types/node globals
stop resolving estate-wide under TS 7 (process, Buffer, node:path,
NodeJS — in config/env.config.ts, core/local-runner/connect.ts,
core/comet/pending-users-registry.ts and a dozen others), all of which are
also on main. This is a toolchain migration for its own PR, not a line to slip
into a QA test PR. Happy to open that PR if you want it.

2. Pre-existing duplicate in the file this PR extends.
core/backend/client.ts defines deleteDashboard twice — at 629 and 1121 on
this branch, 574 and 1066 on main. Pre-existing, surfaced only by the TS 7 run
above. The PR adds 277 lines to that file and does not touch either definition.
Worth folding into the toolchain PR rather than this one.

3. Type health of this PR's own code, checked under a workaround. Because
the gate never reaches the source files, I re-ran tsc against a copy of the
config with baseUrl stripped and filtered to this PR's files. The two new
specs, the POM and the new client code produce zero errors — including
after my edits. That is the closest to a real type check available on this
toolchain; it is not the estate's own gate passing.


What I verified positively

  • Placement. spec_dir: online-evaluation, both specs in
    tests/online-evaluation/, both added to the area's specs: list,
    @area:online-evaluation matching all six neighbours. No finding.
  • list-rules is honestly covered. The spec asserts the project listing
    answers 200 and equals the exact seeded set (not containment — a listing
    that silently dropped the unreadable rows would pass containment and fail
    this), that the server's total agrees, that the workspace-wide read the
    sampler shares also answers 200 with a non-empty total, and that the rules
    page renders one row per rule and nothing else, with no 5xx on the evaluators
    endpoint. The prose control rule is what makes a green listing mean something.
  • Tier. @t2-cuj on both, matching every neighbour but the smoke spec. The
    seeded rules never score — no traces are logged against them — so no LLM
    budget is spent. No tier inflation.
  • Teardown. Both specs request automationRulesCleanup, which is the
    fixture's whole documented API. Rules do not cascade with their project, so
    this is required, and it is a fixture rather than in-body cleanup. Names are
    built from testNamespace, which is cujPrefix-derived, so the estate's
    timestamp sweep will find them. The project fixture is test-scoped and
    freshly created, which is what makes the exact-set listing assertion sound
    rather than flaky.
  • Selectors resolve against real product code. add-edit-rule-dialog-submit
    (AddEditRuleDialog.tsx:786), playground-message-row and
    playground-message-editor (LLMPromptMessage.tsx:239,348) all exist, and
    data-role={role} sits on the same Card as the row testid — so the POM's
    .and() of two attributes is correct rather than a filter({ has }).
    LLM_MESSAGE_ROLE.user === "user" matches the POM's parameter.
  • The CodeMirror read is safe here. readPromptMessageText joins .cm-line
    text nodes. I checked the variable-highlight plugin: {{output}} is a
    Decoration.mark (styling, text preserved), and the only replacing widget is
    the variable hint, which returns Decoration.none unless the facet is set
    while typing. The dialog does not pass compact, so no collapsed-overflow
    editor either. The POM already documents the virtualisation limit for long
    prompts.
  • Support code is bucket 1. New POM methods, new backend-client routes and
    new exported types, all extending established patterns in files that long
    predate this PR. No production code, no test-only hooks needed — the
    frontend already carried every testid these specs use. Nothing outside
    tests_end_to_end/ except the frontend files I read.
  • Blast radius of the shared changes. The POM change is a refactor: a new
    ruleRows getter, with waitForReady and ruleRow rewritten to call it —
    same locator string, no behaviour change for the six existing specs that
    import OnlineEvaluationPage. client.ts and index.ts are pure additions.

Bot comments

bot-comments.json is an empty array — no inline review comments from baz or
any other bot on this PR. Nothing to triage, nothing to reply to.

# Finding Verdict Disposition
(none — no bot comments on this PR)

Estate gates: tag_lint PASS · tsc FAIL (pre-existing on main) · playwright --list PASS

Re-run after my edits:

Gate Result Notes
tag_lint.py --taxonomy coverage/taxonomy.yaml --estate . PASS 61 specs checked, 1 exempt, 0 problems
npx tsc --noEmit FAIL TS5102 baseUrl — identical on origin/main, see Note 1
npx playwright test --list PASS 3 tests in the 2 new files collect; 122 tests in 58 files estate-wide

What I could not verify

  • I did not execute either spec. No Opik stack was available to this review,
    so everything below is reasoning against the source, not a green run.
  • Whether the edit dialog submits cleanly when the seeded model is not from a
    configured provider.
    createLlmJudgeRule defaults to model: 'gpt-4o',
    while the smoke spec picks its model via ensureModelAvailable(page)
    i.e. the environment is not guaranteed to have OpenAI configured. I read the
    form schema and the model field validates as z.string().min(1) only, with no
    provider-availability check, and the submit button is disabled only for the
    code-metric edit block. So this should be fine. But "should be fine from
    reading the zod schema" is not the same as watching the dialog close, and the
    round-trip spec's submitDialog() waits on exactly that.
  • The estate's real tsc behaviour under TS 7. The 94 errors I saw come from
    a config I modified to get past TS5102. Whether the fix is types: ["node"],
    a typescript pin, or something else is for whoever takes the toolchain PR.
  • Whether edit-rule should own content-shape preservation. That is the
    open decision in Should-fix 1, and it is genuinely a judgement about what the
    coverage denominator ought to be — not something I could settle by reading.

review_generated_tests.yml ·
this PR stays a draft — a human decides when it is ready.

});
});

test('A genuinely multimodal judge message still reads back as structured content', { tag: ['@cap:online-evaluation.edit-rule'] }, async ({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Structured test falsely reports edit coverage

The structured contentArray test is tagged edit-rule, and edit-rule is marked covered in tests_end_to_end/coverage/taxonomy.yaml:675-680, even though this test only verifies API-level preservation rather than UI editing; coverage therefore overstates edit capability — should we add a dedicated capability and retag the test, or remove the tag?

Supporting evidence from every grouped finding:

  • tests_end_to_end/coverage/taxonomy.yaml:675-680: The taxonomy marks edit-rule as covered: true even though the content-array behavior has no matching capability and remains unresolved, so coverage reports classify it under edit-rule — should we add a dedicated capability and map the test to it, or remove the behavior from this capability’s coverage and leave it uncovered?

  • tests_end_to_end/e2e/tests/online-evaluation/online-evaluation-judge-prompt-round-trip.spec.ts:94-94: This test is tagged edit-rule even though it only reads back and re-saves multimodal contentArray data, so it overstates online-evaluation's UI-edit coverage — should we add a dedicated kebab-case capability and use it here, or remove the tag?

Severity

Want Baz to fix this for you? Activate Fixer You can also update your AI coding guidelines based on this comment by apply pr to [branch name]

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
`tests_end_to_end/e2e/tests/online-evaluation/online-evaluation-judge-prompt-round-trip.spec.ts`
around lines 94-150, the multimodal judge-message test only performs API read-back and
re-save assertions, but is tagged as `online-evaluation.edit-rule` even though it does
not exercise the edit UI. Add a dedicated kebab-case capability to the project’s
capability taxonomy for multimodal content round-trips, then replace the existing
capability tag on this test with the new one.

Comment on lines +197 to +200
export interface JudgeMessageContentPartRef {
type: string;
text: string | null;
imageUrl: { url: string; detail: string | null } | null;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API response types hide wire field names

These API-facing interfaces rename the wire fields to imageUrl and contentArray in JudgeMessageRef, so their declared response shapes don't match the image_url and content_array JSON the implementation reads — should we use the exact backend keys or document these as normalized client representations instead?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
tests_end_to_end/e2e/core/backend/client.ts around lines 197-200, update
JudgeMessageContentPartRef and the related JudgeMessageRef definition so their names and
documentation accurately reflect whether they model raw backend JSON or a normalized
client result. Prefer using the wire keys image_url and content_array for API-facing
types, or explicitly rename/document these as normalized representations and keep the
conversion in getLlmJudgeMessages consistent. Update any affected consumers and comments
so the declared types no longer contradict the JSON contract.

Comment on lines +219 to +227
/** One judge message as the create endpoint accepts it — set exactly one of the two. */
export interface JudgeMessageWrite {
role: 'SYSTEM' | 'USER';
content?: string;
contentArray?: Array<{
type: string;
text?: string;
image_url?: { url: string; detail?: string };
}>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Judge helper forwards invalid message shapes

JudgeMessageWrite leaves both content and contentArray optional, and createLlmJudgeRule serializes each independently at tests_end_to_end/e2e/core/backend/client.ts:219-227, so callers can send both wire fields or neither and receive a remote rejection or ambiguous payload — should the helper enforce exactly one shape at its boundary?

Supporting evidence from every grouped finding:

  • tests_end_to_end/e2e/core/backend/client.ts:220-227: JudgeMessageWrite accepts neither content nor contentArray, or both, so createLlmJudgeRule can send evaluator-invalid messages and receive a server rejection — should we model it as a union of { role; content: string; contentArray?: never } and { role; content?: never; contentArray: ... }?

  • tests_end_to_end/e2e/core/backend/client.ts:219-227: Both JudgeMessageWrite content fields are optional and createLlmJudgeRule emits them independently, so requests can contain both content and content_array or neither — should we enforce exactly one at the boundary?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
tests_end_to_end/e2e/core/backend/client.ts around lines 219-227, update
`JudgeMessageWrite` and `createLlmJudgeRule` so each judge message must provide exactly
one of `content` or `contentArray`. Model the type as an exclusive/discriminated union
and add a runtime boundary check before `rawFetch` that rejects messages with both
fields or neither, rather than silently emitting an invalid payload.

Comment on lines +1641 to +1644
const { status, message, location } = await rawFetch(
'POST',
'/v1/private/automations/evaluators/',
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New specs bypass public evaluator client

createLlmJudgeRule seeds through rawFetch('POST', '/v1/private/automations/evaluators/'), so the E2E setup bypasses public Opik/suite-client APIs and violates .agents/skills/writing-e2e-tests/conventions.md — should we use sdk.api.automationRuleEvaluators.createAutomationRuleEvaluator as in workspace-role-resource-actions.ts, or record an approved isolated exception for the unsupported message-shape seam?

Severity

Want Baz to fix this for you? Activate Fixer You can also update your AI coding guidelines based on this comment by apply pr to [branch name]

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
tests_end_to_end/e2e/core/backend/client.ts around lines 1641-1644, refactor
`createLlmJudgeRule` so E2E setup does not call the private
`/v1/private/automations/evaluators/` endpoint through `rawFetch`. Use the existing
public `sdk.api.automationRuleEvaluators.createAutomationRuleEvaluator` client/bridge
path and adapt its response handling as needed; if it cannot support the judge message
shape, document and obtain an explicitly approved, isolated exception instead of
silently using `rawFetch`.

Comment on lines +1711 to +1713
// A non-200 is a legitimate result here, not an error to translate: the
// caller asserts on it. Only the shape of a 200 is trusted.
if (status !== 200) return { status, total: 0, names: [] };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Evaluator failures lose diagnostics

The non-200 branch drops the message from rawFetch when replacing the response with { status, total: 0, names: [] }, so evaluator failures expose only a generic expectation mismatch — should we retain the message in AutomationRuleEvaluatorPageRef while preserving the non-throwing status-based behavior?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
tests_end_to_end/e2e/core/backend/client.ts around lines 1711-1713, update
`findAutomationRuleEvaluatorsPage` so non-200 evaluator-list responses preserve the
diagnostic `message` from `rawFetch` instead of returning only status and empty data.
Extend `AutomationRuleEvaluatorPageRef` with an appropriate message field and
destructure/return it in the failure result, while keeping the existing status-based,
non-throwing behavior and ensuring successful responses still satisfy the type.

Comment on lines +1808 to +1815
body: {
type: rule.type,
name: rule.name,
project_ids: [projectId],
sampling_rate: rule.sampling_rate,
enabled: rule.enabled,
trigger_scope: rule.trigger_scope,
code: rule.code,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No-op resave destroys evaluator state

resaveAutomationRuleFromReadBack reconstructs an incomplete PATCH at tests_end_to_end/e2e/core/backend/client.ts:1808-1815: it omits persisted filters and sends project_ids: [projectId] instead of the complete association set, so a no-op replay clears filters and detaches a multi-project evaluator from every other project — should the helper derive both values from the complete GET state, or explicitly reject unsupported rule shapes?

Supporting evidence from every grouped finding:

  • tests_end_to_end/e2e/core/backend/client.ts:1804-1815: resaveAutomationRuleFromReadBack omits filters from its PATCH body, so the backend maps it to null and replaying a filtered evaluator clears automation_rules.filters — should we include the read-back filters in the payload?

  • tests_end_to_end/e2e/core/backend/client.ts:1808-1815: The read-back PATCH sends project_ids: [projectId] instead of the rule’s existing project set, so the backend replaces all junction rows with that single association and replaying a multi-project evaluator detaches it from the other projects — should we preserve the full association set?

  • tests_end_to_end/e2e/core/backend/client.ts:1809-1812: resaveAutomationRuleFromReadBack always sends project_ids: [projectId], so a no-op read/save drops the rule's other project associations and narrows its scope — should we preserve project_ids from the read response or reject multi-project rules?

  • tests_end_to_end/e2e/core/backend/client.ts:1808-1815: resaveAutomationRuleFromReadBack rebuilds an exact PATCH without persisted filters and with project_ids: [projectId], so the backend clears saved filters and detaches other projects from multi-project rules. Should we derive both from the complete GET state, including projects, or explicitly limit and test the helper’s supported rule shape?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
`tests_end_to_end/e2e/core/backend/client.ts` around lines 1789-1815, fix
`resaveAutomationRuleFromReadBack` so its PATCH preserves the evaluator’s complete
persisted state instead of reconstructing an incomplete update. Include the `filters`
returned by GET and derive `project_ids` from the evaluator’s full project association
list rather than `[projectId]`; update the response typing and add coverage for filters
and multi-project rules, or explicitly reject unsupported rule shapes.

// on those would make this spec a monitor for someone else's endpoint.
const evaluatorServerErrors: string[] = [];
page.on('response', (res) => {
if (res.url().includes('/automations/evaluators') && res.status() >= 500) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broad listener reports unrelated 5xx responses

The UI wire assertion matches the evaluator collection by substring, so it also accepts other origins, /evaluators-backup, query-string occurrences, and detail/log subroutes — should we parse the URL and require the configured API origin plus /v1/private/automations/evaluators with a path boundary and optional trailing slash/query?

Severity

Want Baz to fix this for you? Activate Fixer You can also update your AI coding guidelines based on this comment by apply pr to [branch name]

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
`tests_end_to_end/e2e/tests/online-evaluation/online-evaluation-json-looking-judge-prompts.spec.ts`
around lines 153-153, tighten the evaluator response filter in the UI wire assertion.
Parse each response URL and require the configured API origin and the exact
`/v1/private/automations/evaluators` collection pathname, allowing only an optional
trailing slash and query string; do not match other origins, similarly named paths, or
evaluator detail/log subroutes.

Comment on lines +675 to +680
# OPEN, needs a human call: the content_array test asserts judge-message
# content-SHAPE preservation, which no capability in this area names. It
# rides on edit-rule for want of a better key. Either add a capability for
# it or drop that tag and leave the behaviour honestly uncovered — do not
# let it sit here as a silent widening of edit-rule.
edit-rule: { covered: true, tier: t2-cuj, note: "plain-string prompt: edit-dialog hydration + no-op re-save, byte-for-byte; content_array prompt: API-only read-then-write re-save, no dialog gesture; editing a field through the dialog is not covered" }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage taxonomy falsely reports edit coverage

edit-rule is marked covered: true even though field editing remains untested, so coverage gates and reviewers may treat rename/model/prompt edits as covered based only on hydration and no-op re-save — should we add a field-edit assertion, or keep it uncovered and add a narrower prompt round-trip capability?

Severity

Want Baz to fix this for you? Activate Fixer

Other fix methods

Fix in Cursor

Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
`tests_end_to_end/coverage/taxonomy.yaml` around lines 675-680, correct the `edit-rule`
coverage declaration so it does not claim field editing is covered when the tests only
perform prompt hydration and no-op re-save round trips. Either add an end-to-end
assertion that renames a rule, changes its model, or rewrites its prompt and then keep
`edit-rule` covered, or mark `edit-rule` uncovered and introduce a narrower capability
(such as prompt round-trip preservation) with an accurate note.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔴 size/XL tests Including test files, or tests related like configuration. typescript *.ts *.tsx

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant