Skip to content

Render \ce chemistry + \pu units in docs and chat, and harden AI math instructions - #722

Merged
urjitc merged 5 commits into
mainfrom
fix/math-doc-chat-rendering
Aug 3, 2026
Merged

Render \ce chemistry + \pu units in docs and chat, and harden AI math instructions#722
urjitc merged 5 commits into
mainfrom
fix/math-doc-chat-rendering

Conversation

@urjitc

@urjitc urjitc commented Aug 3, 2026

Copy link
Copy Markdown
Member

What

Math authoring across both surfaces (documents and AI chat) now renders chemistry (\ce{…}) and units (\pu{…}), and the model is guided to write correct math for each surface. Everything was verified end-to-end through the real pipelines + KaTeX.

Why

Three problems, all confirmed with evals across Claude tiers + the real doc/chat render pipelines:

  1. Chemistry & units didn't render. KaTeX's mhchem extension wasn't imported, so \ce{}/\pu{} came out as error-colored source text.
  2. \ce/\pu were broken in chat even after importing mhchem — because @streamdown/math bundles its own katex@0.16.x, a different instance than the app's 0.17.0 that the mhchem import patches. Proven by running Streamdown's actual rehype-katex (the source was painted in the errorColor, same as a genuinely-undefined command).
  3. The AI made ThinkEx-specific math mistakes the instructions didn't cover: wrong delimiters (\(…\)), currency \$ escaping bleeding into HTML docs (visible backslash), and <sub>/<sup> tags that reject the whole write.

How

  • import "katex/contrib/mhchem" in the doc editor (tiptap-extensions.ts) and chat renderer (AiChatMessageResponse.tsx).
  • Dedupe KaTeX to a single instance via a pnpm-workspace.yaml override (katex: 0.17.0) so the one mhchem import patches the instance both surfaces render with. 0.17.0 satisfies both consumers (tiptap-math peer ^0.16.4 || ^0.17.0, rehype-katex ^0.16).
  • Tighten the AI instructions (single shared workspaceDocumentHtmlInstruction + the chat soul prompt): use data-latex (never $…$/\(…\)) in docs, plain $30 currency in HTML, sub/superscripts as math, and \ce/\pu for chemistry/units. Also removed two redundancies (a "KaTeX supports matrices/…" hint models already follow, and a doc-ruleset that was duplicated in the create tool's initialContent field).

Verification

  • Create + edit paths, 4 Claude tiers: post-fix, 0 rejects / 0 stray-backslash / real math nodes on every edit (was: rejects + broken math before).
  • Doc + chat render battery (12 constructs × 2 tiers): 24/24 render — matrices, aligned, cases, integrals, sums, normal PDF, cross-product determinant, \ce (states/->[H2O]/equilibrium), \pu units.
  • Hard tail (24 constructs): 24/24 — nuclear isotopes, tensor indices, bra-ket, continued fractions; both tiers avoided every unsupported physics-package macro on their own (evidence a runtime KaTeX doc-fetch isn't needed).
  • pnpm lint (types + lint) green.

Commits

  • feat(workspaces): render \ce chemistry and \pu units via KaTeX mhchem — imports + katex dedupe
  • fix(workspaces): tighten AI doc/chat math authoring instructions — prompt hardening + de-dup
  • chore(deps): dedupe katex to 0.17.0 in the lockfile — lockfile resolution (also carries the vitest-evals devDep added by the vp config prepare hook)
  • feat(ai): add workspace agent eval harness — related eval scaffolding

Notes for reviewers

  • The instruction is one shared constant used by both the create and edit tool descriptions, so the guidance change propagates to both paths.
  • No security posture change: KaTeX trust stays false (LLM/user-derived content), so \href/\html* remain disabled.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

Summary by CodeRabbit

  • New Features
    • AI responses now support chemistry notation and unit quantities with proper formatting.
    • Workspace document tools provide clearer guidance for mathematical, chemical, unit, and currency formatting.
  • Bug Fixes
    • Improved consistency and reliability across workspace operations by standardizing operation error handling.
  • Tests
    • Added automated checks for workspace tool schemas, prompt behavior, tool usage, and response quality.
  • Documentation
    • Added guidance for running workspace AI evaluations and contributing evaluation cases.

urjitc added 4 commits August 3, 2026 12:31
Import katex/contrib/mhchem in the doc editor and chat renderer, and pin
katex to one version via a pnpm override so the extension patches the
single instance both surfaces render with. Without the dedupe,
rehype-katex used its own katex copy and \ce/\pu fell back to
error-colored source text in chat.
Teach the model that chemistry (\ce) and units (\pu) render, and drop
two redundancies: the "KaTeX also supports matrices/aligned/..." hint
(models already produce those unprompted) and the doc-HTML ruleset that
was repeated in the create initialContent field (the create tool
description already carries it, matching edit).
Lockfile resolution for the KaTeX single-instance override. Also carries
the vitest-evals devDependency the `vp config` prepare hook added during
install, kept so package.json and the lockfile stay consistent.
Live evals (pnpm eval) grade tool choice, argument validity, and answer
quality against real models through the gateway, run in the workers pool
alongside the app's own worker tests.

Add a free, zero-token snapshot test of the model-facing surface (system
prompt + tool input JSON schemas) that runs in normal CI.

Move the operation failure-code consts into a pure leaf module so the tool
schema layer no longer depends on the operation impls, which keeps it
Node-safe for the snapshot test.
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@capy-ai

capy-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

React Doctor found 1 new issue in 1 file · 1 warning · score 92 / 100 (Great) · 0 fixed · vs main

1 warning

eval/support/scorers.ts

  • ⚠️ L78 Chained array iterations js-combine-iterations

Reviewed by React Doctor for commit 3193b7b. See inline comments for fixes.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@urjitc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 745c0b2e-b4f5-41f5-9119-fdadf12b9562

📥 Commits

Reviewing files that changed from the base of the PR and between 052d97c and 3193b7b.

📒 Files selected for processing (5)
  • eval/datasets/workspace-tools.cases.ts
  • eval/support/harness.ts
  • eval/support/scorers.ts
  • eval/workspace-tools.eval.ts
  • src/features/workspaces/ai/ai-thread-tool.ts
📝 Walkthrough

Walkthrough

This PR centralizes workspace operation failure codes, adds chemistry and unit notation support, and introduces snapshot and live AI evaluations for workspace tools.

Changes

Workspace AI evaluation and tooling

Layer / File(s) Summary
Workspace contracts and rendering
src/features/workspaces/operations/*, src/features/workspaces/ai/*, src/features/workspaces/components/ai-chat/*, src/features/workspaces/documents/*
Workspace failure codes use a shared module. Tool descriptions define supported document markup. KaTeX and prompt guidance support chemistry and unit notation. Snapshot tests cover the workspace prompt and tool schemas.
Evaluation runtime and agent harness
tsconfig.json, vitest.evals.config.ts, package.json, eval/support/harness.ts
The eval project uses the Cloudflare Worker runtime. Package scripts run the eval suite. The harness runs bounded workspace-agent generations with schema-backed tools and normalized outputs.
Evaluation cases and scoring
eval/datasets/*, eval/support/scorers.ts, eval/workspace-tools.eval.ts, eval/README.md, pnpm-workspace.yaml
The dataset covers workspace tool selection and answer quality. Scorers validate tool calls and optional rubric verdicts. Live tests skip without an API key. The README documents the evaluation workflow. KaTeX is pinned to 0.17.0.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EvaluationSuite
  participant WorkspaceAgent
  participant WorkspaceTools
  participant AIGateway
  participant Scorers
  EvaluationSuite->>WorkspaceAgent: Run dataset case
  WorkspaceAgent->>WorkspaceTools: Load workspace tool schemas
  WorkspaceAgent->>AIGateway: Request model generation
  AIGateway-->>WorkspaceAgent: Return text and tool calls
  WorkspaceAgent-->>EvaluationSuite: Return normalized output
  EvaluationSuite->>Scorers: Check inputs and tool usage
  Scorers->>AIGateway: Grade rubric answer when configured
  AIGateway-->>Scorers: Return structured score
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary rendering and AI instruction changes for chemistry notation and units.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/math-doc-chat-rendering

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 052d97c256

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread eval/support/harness.ts Outdated
description: definition.description,
// Heterogeneous across tools; the eval only needs the schema rendered
// to the model, so widen past the per-tool union.
inputSchema: asSchema(definition.inputSchema as z.ZodTypeAny),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use provider-compatible schemas in the eval

When a case selects claude-sonnet or claude-haiku through the supported modelId field, this sends the raw Zod schemas, several of which emit maxItems. Production deliberately removes that keyword in createProviderCompatibleInputSchema because Anthropic-compatible providers reject it, so these evals can fail at the gateway instead of grading model behavior. Reuse the production schema adapter rather than wrapping definition.inputSchema directly.

Useful? React with 👍 / 👎.

Comment thread eval/support/harness.ts Outdated
Comment on lines +59 to +60
// Neutral stub so a follow-up step (e.g. read → edit) can still proceed.
execute: async () => ({ ok: true, note: "eval stub — no real mutation" }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Return realistic results for multi-step tool evals

For the added read-then-edit case, workspace_read_items returns this generic object instead of document HTML and a valid data-ref, so the model cannot construct the requested content-preserving edit from the first step. Since the scorer accepts any nonempty ref as schema-valid, a hallucinated target can still make the case pass, while a model that correctly refuses to edit without content can fail. Use per-tool, schema-valid fixtures—especially a read result containing editable HTML and refs—for multi-step cases.

Useful? React with 👍 / 👎.

Comment thread eval/support/harness.ts Outdated
Comment on lines +54 to +58
tool({
description: definition.description,
// Heterogeneous across tools; the eval only needs the schema rendered
// to the model, so widen past the per-tool union.
inputSchema: asSchema(definition.inputSchema as z.ZodTypeAny),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include production tool input examples in evals

The production workspace adapter passes definition.inputExamples into every tool, and the shared language-model middleware injects those examples into the model prompt, but these reconstructed tools omit them. Consequently the live eval exercises a materially different tool surface, cannot detect regressions to the examples, and may report selection or argument-validity failures that do not reproduce in the app. Pass through definition.inputExamples or reuse the production tool factory.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
src/features/workspaces/operations/workspace-operation-failure-codes.ts (1)

1-7: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Keep this Node-eval contract module dependency-free.

workspace-operation-failure-codes.ts is documented as a pure leaf, but importing it evaluates document-ai-edits.ts, which imports @tiptap/pm/model and zod. Move the operation/vocabulary-level failure-code arrays into a separate dependency-free file and keep ProseMirror/Zod-related files out of this Node eval path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/workspaces/operations/workspace-operation-failure-codes.ts`
around lines 1 - 7, Move the workspace operation and vocabulary failure-code
arrays currently sourced through documentAiEditFailureCodes and
workspaceRelationFailureCodes into a separate dependency-free module. Update
workspace-operation-failure-codes.ts and all consumers to import those arrays
from the new leaf module, ensuring document-ai-edits.ts and other
ProseMirror/Zod-dependent files are not evaluated by the Node-facing contract
path.
src/features/workspaces/operations/workspace-tool-surface.test.ts (1)

5-36: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive the tool list from workspaceToolDefinitions to avoid drift.

This file re-enumerates workspace tool names and schemas by hand. eval/support/harness.ts already treats workspaceToolDefinitions as the canonical list of tools (name + description + inputSchema). If a tool is added, renamed, or removed there without a matching manual update here, this snapshot test silently stops covering it — undermining the regression-net purpose the file's own comment describes.

Build TOOL_INPUT_SCHEMAS from workspaceToolDefinitions instead of importing each schema individually.

♻️ Proposed refactor
-import { getAIThreadSoulPrompt } from "`#/features/workspaces/ai/ai-thread-soul-prompt`";
-import {
-	workspaceCreateItemsInputSchema,
-	workspaceDeleteItemsInputSchema,
-	workspaceEditItemInputSchema,
-	workspaceLinkItemsInputSchema,
-	workspaceListItemsInputSchema,
-	workspaceMoveItemsInputSchema,
-	workspaceReadItemsInputSchema,
-	workspaceRenameItemInputSchema,
-	workspaceSearchInputSchema,
-} from "`#/features/workspaces/operations/workspace-tool-schemas`";
+import { getAIThreadSoulPrompt } from "`#/features/workspaces/ai/ai-thread-soul-prompt`";
+import { workspaceToolDefinitions } from "`#/features/workspaces/operations/workspace-tool-definitions`";
...
-const TOOL_INPUT_SCHEMAS = {
-	workspace_list_items: workspaceListItemsInputSchema,
-	workspace_read_items: workspaceReadItemsInputSchema,
-	workspace_search: workspaceSearchInputSchema,
-	workspace_create_items: workspaceCreateItemsInputSchema,
-	workspace_edit_item: workspaceEditItemInputSchema,
-	workspace_delete_items: workspaceDeleteItemsInputSchema,
-	workspace_move_items: workspaceMoveItemsInputSchema,
-	workspace_rename_item: workspaceRenameItemInputSchema,
-	workspace_link_items: workspaceLinkItemsInputSchema,
-} as const;
+const TOOL_INPUT_SCHEMAS = Object.fromEntries(
+	workspaceToolDefinitions.map((definition) => [definition.name, definition.inputSchema]),
+);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/workspaces/operations/workspace-tool-surface.test.ts` around
lines 5 - 36, Update workspace-tool-surface.test.ts to derive TOOL_INPUT_SCHEMAS
from the canonical workspaceToolDefinitions collection, using each definition’s
name and inputSchema rather than manually importing and enumerating individual
schemas. Remove the redundant per-schema imports while preserving the existing
tool-schema snapshot coverage and key format.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/features/workspaces/operations/workspace-operation-failure-codes.ts`:
- Around line 1-7: Move the workspace operation and vocabulary failure-code
arrays currently sourced through documentAiEditFailureCodes and
workspaceRelationFailureCodes into a separate dependency-free module. Update
workspace-operation-failure-codes.ts and all consumers to import those arrays
from the new leaf module, ensuring document-ai-edits.ts and other
ProseMirror/Zod-dependent files are not evaluated by the Node-facing contract
path.

In `@src/features/workspaces/operations/workspace-tool-surface.test.ts`:
- Around line 5-36: Update workspace-tool-surface.test.ts to derive
TOOL_INPUT_SCHEMAS from the canonical workspaceToolDefinitions collection, using
each definition’s name and inputSchema rather than manually importing and
enumerating individual schemas. Remove the redundant per-schema imports while
preserving the existing tool-schema snapshot coverage and key format.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 19dfe2ce-af52-400b-8bb8-3c1339e1cc7f

📥 Commits

Reviewing files that changed from the base of the PR and between eeefbe0 and 052d97c.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/features/workspaces/operations/__snapshots__/workspace-tool-surface.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (21)
  • eval/README.md
  • eval/datasets/workspace-tools.cases.ts
  • eval/support/harness.ts
  • eval/support/scorers.ts
  • eval/workspace-tools.eval.ts
  • package.json
  • pnpm-workspace.yaml
  • src/features/workspaces/ai/ai-thread-soul-prompt.ts
  • src/features/workspaces/components/ai-chat/AiChatMessageResponse.tsx
  • src/features/workspaces/documents/tiptap-extensions.ts
  • src/features/workspaces/operations/create-items.ts
  • src/features/workspaces/operations/delete-items.ts
  • src/features/workspaces/operations/edit-item.ts
  • src/features/workspaces/operations/link-items.ts
  • src/features/workspaces/operations/move-items.ts
  • src/features/workspaces/operations/rename-item.ts
  • src/features/workspaces/operations/workspace-operation-failure-codes.ts
  • src/features/workspaces/operations/workspace-tool-schemas.ts
  • src/features/workspaces/operations/workspace-tool-surface.test.ts
  • tsconfig.json
  • vitest.evals.config.ts

@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change enables mhchem rendering in workspace documents and chat, expands workspace-agent math guidance, centralizes operation failure codes, and adds agent evaluation coverage. The new read-then-edit evaluation can report success even when the agent has not received document HTML or a valid item reference: the read stub returns only a neutral success object, while fabricated refs and full-document replacements satisfy the current checks. This leaves targeted document-edit regressions undetected.

Confidence Score: 4/5

Not safe to merge until the targeted-edit evaluation verifies read-derived document references rather than only tool names and input shape.

Focused execution reproduced a passing read-then-edit sequence with no document data, a fabricated item reference, and a full-document replacement. This makes the new evaluation unable to detect a meaningful class of workspace-agent editing regressions.

Files Needing Attention: eval/support/harness.ts needs a realistic workspace_read_items fixture, and eval/workspace-tools.eval.ts plus eval/support/scorers.ts need assertions that connect the returned refs to the requested targeted edit.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for a posted P1 finding, including a focused validation test source for the read-stub grading gap.
  • An initial focused validation run was captured in the logs.
  • The focused validation output showed a neutral read and permissive scoring.
  • Live workspace evaluation was skipped because gateway credentials were not available.
  • The general contract validation documented exact code paths and the targeted workspace-tools case, and described the observed permissive evaluation outcomes including readResult and related validity flags.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 Targeted read-then-edit eval can pass with no document data and fabricated edit provenance

    • Bug
      • The targeted eval requires a read and edit tool call, but its read executor returns no results, HTML, or data-ref values. The grading only verifies that the two names were called and that the edit input has schema-valid shape. The executed focused test proved that a fabricated ref and a replace_all edit are schema-valid and that the same call sequence receives perfect deterministic scores.
    • Cause
      • eval/support/harness.ts:60 applies one neutral executor to all tools, including workspace_read_items; eval/workspace-tools.eval.ts:19-32 delegates grading to input validity and expected-tool checks without asserting a read result or validating targeted refs against returned HTML. The edit schema at src/features/workspaces/documents/document-ai-edits.ts:20-45 cannot establish provenance because ref is only constrained to a nonempty string.
    • Fix
      • Give workspace_read_items an eval fixture response containing a document HTML block and real data-ref, then make the targeted case assert that the edit is a targeted operation whose ref is present in that read response. Reject or separately flag replace_all for this read-first targeted scenario, and validate the produced edit against the fixture before passing the case.

    T-Rex Ran code and verified through T-Rex

Fix All in Cursor

Reviews (1): Last reviewed commit: "feat(ai): add workspace agent eval harne..." | Re-trigger Greptile

Comment thread eval/support/harness.ts Outdated
// to the model, so widen past the per-tool union.
inputSchema: asSchema(definition.inputSchema as z.ZodTypeAny),
// Neutral stub so a follow-up step (e.g. read → edit) can still proceed.
execute: async () => ({ ok: true, note: "eval stub — no real mutation" }),

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.

P1 Read stub removes targeted-edit provenance

workspace_read_items receives the same { ok: true } response as every mutating tool, so the read-then-edit evaluation never supplies HTML or the data-ref values required to make a targeted edit. The grading only checks that workspace_read_items and workspace_edit_item were called with schema-valid inputs; a fabricated nonempty ref, or a replace_all edit that does not use a ref, receives a passing score. As a result, this evaluation can pass while the model cannot perform the production read-to-targeted-edit workflow it is intended to cover.

Return a deterministic read fixture containing document HTML and real data-ref values, then require the targeted scenario to submit a targeted operation whose ref is present in that fixture. Treat replace_all as a separate scenario rather than a passing substitute for a targeted edit.

Artifacts

Focused validation test source for the read-stub grading gap

  • The executed Vitest source invokes the current read stub and checks fabricated-reference, replace-all, and scorer behavior; it reproduces the missing-provenance coverage gap.

Initial focused validation run

  • The initial worker-pool Vitest execution completed one focused test successfully against the current harness; it establishes that the changed-stack test is executable.

Focused validation output showing neutral read and permissive scoring

  • The verbose worker-pool Vitest run printed the neutral read result and showed fabricated references, replace-all, expected tools, and input validity all passing; it confirms the eval gap.

Live workspace eval skipped without gateway credentials

  • The actual live eval command detected no `AI_GATEWAY_API_KEY` and skipped all six cases; it shows why a real gateway model turn could not be run in this environment.

View artifacts

T-Rex Ran code and verified through T-Rex

Fix in Cursor

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

5 issues found across 23 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="eval/datasets/workspace-tools.cases.ts">

<violation number="1" location="eval/datasets/workspace-tools.cases.ts:48">
P2: The “read then edit” eval does not detect the ordering failure it is intended to catch: any sequence containing both tools passes. Adding an ordered tool-sequence assertion (or a case-specific scorer) would verify that `workspace_read_items` occurs before `workspace_edit_item`.</violation>

<violation number="2" location="eval/datasets/workspace-tools.cases.ts:61">
P2: A read-only turn can still call `workspace_link_items` and pass this case, even though the injected scope explicitly forbids linking items. Including the link tool in `forbiddenTools` would make the test enforce the stated boundary.</violation>

<violation number="3" location="eval/datasets/workspace-tools.cases.ts:75">
P2: The general-question case does not enforce its no-tool requirement because five workspace tools are missing from `forbiddenTools`. Listing every workspace tool (or adding an explicit no-tool assertion) would prevent tool calls from being silently accepted.</violation>
</file>

<file name="eval/support/scorers.ts">

<violation number="1" location="eval/support/scorers.ts:1">
P2: The deterministic scorers are no longer portable outside the Cloudflare Workers test pool because this module has a top-level `cloudflare:test` dependency. Keeping pure scorers in a dependency-free module and isolating or injecting the worker-specific judge dependencies would preserve the portability documented in `eval/README.md`.</violation>

<violation number="2" location="eval/support/scorers.ts:88">
P2: A model-generated answer can inject instructions into the LLM judge and make a failing evaluation pass. Serializing the fields as quoted data and explicitly telling the judge to ignore instructions in `userPrompt` and `assistantAnswer` would make the grading path resistant to this failure mode.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

prompt:
"In /Notes/Standup.md, add a second bullet that says 'Review metrics'. Read the document first, then make the edit.",
},
expectedTools: ["workspace_read_items", "workspace_edit_item"],

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.

P2: The “read then edit” eval does not detect the ordering failure it is intended to catch: any sequence containing both tools passes. Adding an ordered tool-sequence assertion (or a case-specific scorer) would verify that workspace_read_items occurs before workspace_edit_item.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At eval/datasets/workspace-tools.cases.ts, line 48:

<comment>The “read then edit” eval does not detect the ordering failure it is intended to catch: any sequence containing both tools passes. Adding an ordered tool-sequence assertion (or a case-specific scorer) would verify that `workspace_read_items` occurs before `workspace_edit_item`.</comment>

<file context>
@@ -0,0 +1,80 @@
+			prompt:
+				"In /Notes/Standup.md, add a second bullet that says 'Review metrics'. Read the document first, then make the edit.",
+		},
+		expectedTools: ["workspace_read_items", "workspace_edit_item"],
+	},
+	{
</file context>

"workspace_create_items",
"workspace_edit_item",
"workspace_delete_items",
"workspace_search",

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.

P2: The general-question case does not enforce its no-tool requirement because five workspace tools are missing from forbiddenTools. Listing every workspace tool (or adding an explicit no-tool assertion) would prevent tool calls from being silently accepted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At eval/datasets/workspace-tools.cases.ts, line 75:

<comment>The general-question case does not enforce its no-tool requirement because five workspace tools are missing from `forbiddenTools`. Listing every workspace tool (or adding an explicit no-tool assertion) would prevent tool calls from being silently accepted.</comment>

<file context>
@@ -0,0 +1,80 @@
+			"workspace_create_items",
+			"workspace_edit_item",
+			"workspace_delete_items",
+			"workspace_search",
+		],
+		qualityRubric:
</file context>

"workspace_create_items",
"workspace_edit_item",
"workspace_move_items",
"workspace_rename_item",

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.

P2: A read-only turn can still call workspace_link_items and pass this case, even though the injected scope explicitly forbids linking items. Including the link tool in forbiddenTools would make the test enforce the stated boundary.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At eval/datasets/workspace-tools.cases.ts, line 61:

<comment>A read-only turn can still call `workspace_link_items` and pass this case, even though the injected scope explicitly forbids linking items. Including the link tool in `forbiddenTools` would make the test enforce the stated boundary.</comment>

<file context>
@@ -0,0 +1,80 @@
+			"workspace_create_items",
+			"workspace_edit_item",
+			"workspace_move_items",
+			"workspace_rename_item",
+		],
+		qualityRubric:
</file context>

Comment thread eval/support/scorers.ts
@@ -0,0 +1,97 @@
import { env } from "cloudflare:test";

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.

P2: The deterministic scorers are no longer portable outside the Cloudflare Workers test pool because this module has a top-level cloudflare:test dependency. Keeping pure scorers in a dependency-free module and isolating or injecting the worker-specific judge dependencies would preserve the portability documented in eval/README.md.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At eval/support/scorers.ts, line 1:

<comment>The deterministic scorers are no longer portable outside the Cloudflare Workers test pool because this module has a top-level `cloudflare:test` dependency. Keeping pure scorers in a dependency-free module and isolating or injecting the worker-specific judge dependencies would preserve the portability documented in `eval/README.md`.</comment>

<file context>
@@ -0,0 +1,97 @@
+import { env } from "cloudflare:test";
+import { Output, generateText } from "ai";
+import { z } from "zod";
</file context>

Comment thread eval/support/scorers.ts
output: Output.object({ schema: QUALITY_VERDICT_SCHEMA }),
system:
"You are a strict grader. Score how well the ASSISTANT ANSWER satisfies the RUBRIC for the given USER PROMPT. Return pass=false unless the rubric is clearly met. score is 0..1.",
prompt: `USER PROMPT:\n${args.prompt}\n\nASSISTANT ANSWER:\n${args.answer}\n\nRUBRIC:\n${args.rubric}`,

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.

P2: A model-generated answer can inject instructions into the LLM judge and make a failing evaluation pass. Serializing the fields as quoted data and explicitly telling the judge to ignore instructions in userPrompt and assistantAnswer would make the grading path resistant to this failure mode.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At eval/support/scorers.ts, line 88:

<comment>A model-generated answer can inject instructions into the LLM judge and make a failing evaluation pass. Serializing the fields as quoted data and explicitly telling the judge to ignore instructions in `userPrompt` and `assistantAnswer` would make the grading path resistant to this failure mode.</comment>

<file context>
@@ -0,0 +1,97 @@
+		output: Output.object({ schema: QUALITY_VERDICT_SCHEMA }),
+		system:
+			"You are a strict grader. Score how well the ASSISTANT ANSWER satisfies the RUBRIC for the given USER PROMPT. Return pass=false unless the rubric is clearly met. score is 0..1.",
+		prompt: `USER PROMPT:\n${args.prompt}\n\nASSISTANT ANSWER:\n${args.answer}\n\nRUBRIC:\n${args.rubric}`,
+	});
+
</file context>

Comment thread eval/support/harness.ts
Comment thread eval/support/harness.ts Outdated
Comment thread eval/support/harness.ts Outdated
The eval hand-rolled its toolset instead of the production path, so it
diverged in three ways the reviewers caught:

- Sent raw Zod schemas (with `maxItems`, which Anthropic-compatible
  providers reject) instead of the provider-compatible schema. Now reuses
  the exported `createProviderCompatibleInputSchema`.
- Omitted `inputExamples`, which the gateway middleware injects into the
  prompt in production. Now passed through per tool.
- Gave `workspace_read_items` the same neutral `{ok:true}` stub as every
  tool, so the read→edit case could pass with a fabricated ref or a
  whole-doc `replace_all`. Now returns a fixture with real document HTML +
  `data-ref`s, and `scoreTargetedEditProvenance` requires a targeted edit
  whose ref traces back to that fixture.

Also collapses a two-pass map().filter() in scoreNoForbiddenTools.
Comment thread eval/support/scorers.ts
validRefs: string[],
): ScoreResult {
const refs = new Set(validRefs);
const edits = output.toolCalls

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

React Doctor · react-doctor/js-combine-iterations (warning)

This loops over your list twice because .filter().flatMap() makes two passes, so do it in one pass with .reduce() or a for...of loop

Fix → Combine .map().filter() style chains into one pass with .reduce() or a for...of loop, so you only loop over the list once

Docs

@urjitc
urjitc merged commit f2d44c6 into main Aug 3, 2026
12 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Dev Board Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant