Refine observability UI for v0.2.0 - #1
Conversation
📝 WalkthroughWalkthroughThe change removes configurable redaction patterns across contracts, telemetry, APIs, and database storage. It adds observability UI components and quality-gate workflows. It redesigns project and authentication screens, updates fixtures and tests, and increments the package version. ChangesRedaction configuration removal
Observability interface updates
Project and authentication surfaces
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant AppHeader
participant EvaluationRunsView
participant EvaluationRunDetailView
participant EvaluationStatusBadge
User->>AppHeader: Open an evaluation route
AppHeader->>EvaluationRunsView: Link evaluation breadcrumb to runs
User->>EvaluationRunsView: Select a run
EvaluationRunsView->>EvaluationRunDetailView: Open run details
EvaluationRunDetailView->>EvaluationStatusBadge: Render run and outcome statuses
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
|
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. |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
packages/db/migrations/postgres/0007_remarkable_the_fallen.sql (1)
1-1: 🩺 Stability & Availability | 🔵 TrivialVerify the rollout order before dropping the column.
ALTER TABLE "projects" DROP COLUMN "redaction_patterns"is destructive. If a v0.1.0 API instance still runs after migration 0007, project queries or writes can fail because the column no longer exists. Apply this migration after old instances stop using the column, or use an expand/contract rollout.#!/bin/bash set -euo pipefail # Historical migrations may match. Inspect all remaining runtime references. rg -n --hidden --glob '!.git/**' --glob '!node_modules/**' \ 'redaction_patterns|redactionPatterns' . || true # Confirm that deployment upgrades or stops old API instances before migration 0007. rg -n --hidden --glob '!.git/**' --glob '!node_modules/**' \ '0007_remarkable_the_fallen|drizzle|migration|migrate|rollout|deploy|docker|compose' . || true🤖 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 `@packages/db/migrations/postgres/0007_remarkable_the_fallen.sql` at line 1, Verify the rollout sequence for migration 0007 before applying the destructive redaction_patterns drop: confirm no v0.1.0 API instances or other runtime references still read or write redaction_patterns/redactionPatterns, and ensure deployment stops or upgrades those instances before this migration runs; otherwise use an expand/contract migration sequence.apps/web/src/modules/observability/components/evaluation-runs-view.tsx (1)
247-261: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueShow the sort direction visually.
The icon only changes color when the column is active. Sighted users cannot tell ascending from descending. Screen-reader users get the direction from
aria-sort. Render a direction-specific icon (for exampleArrowUp/ArrowDown) when the column is active, and keepArrowsDownUpfor inactive columns.🤖 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 `@apps/web/src/modules/observability/components/evaluation-runs-view.tsx` around lines 247 - 261, Update the sort icon rendering in the Button around the sort(field) handler: show ArrowUp or ArrowDown based on props.filters.order when column is active, and use ArrowsDownUp for inactive columns. Preserve the existing active-column styling and aria-label behavior.apps/web/src/modules/observability/components/quality-gates-view.tsx (3)
446-461: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueChanging the measure reinterprets the stored value.
percentagedepends onrule.measure. The measure select changes onlymeasureand keeps the stored number. A pass rate of0.9shown as "90" becomes an average score shown as "0.9" after the user picks "Average score". The displayed number changes without the user editing it. Reset the value to the type default when the measure crosses the percentage boundary, or state the unit next to the field so the change is expected.🤖 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 `@apps/web/src/modules/observability/components/quality-gates-view.tsx` around lines 446 - 461, Update the measure change handler in the rule editor so switching between “pass_rate” and “average_score” also resets the stored value to the appropriate type default when crossing the percentage boundary, preventing the existing value from being reinterpreted under the new measure.
366-382: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winSwitching the check type discards the metric name.
ruleForTypebuilds a fresh rule and resetsmetricNameto"".evaluation_thresholdandevaluation_regressionboth carrymetricNameandmeasure. A user who switches between these two types loses the values already typed. Carry the shared fields across the change.♻️ Proposed fix
-function ruleForType(type: QualityGateRule["type"]): QualityGateRule { +function ruleForType(type: QualityGateRule["type"], previous?: QualityGateRule): QualityGateRule { + const carried = + previous && previous.type !== "operational_regression" + ? { metricName: previous.metricName, measure: previous.measure } + : {}; if (type === "evaluation_regression") { return { type, metricName: "", measure: "pass_rate", + ...carried, direction: "decrease", maxAbsoluteChange: 0.05, }; } if (type === "operational_regression") { return { type, measure: "p95_latency_ms", maxIncreasePercent: 15 }; } - return defaultRule(); + return { ...defaultRule(), ...carried }; }Then pass the current rule at the call site:
- props.onChange(ruleForType(event.target.value as QualityGateRule["type"])) + props.onChange(ruleForType(event.target.value as QualityGateRule["type"], rule))🤖 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 `@apps/web/src/modules/observability/components/quality-gates-view.tsx` around lines 366 - 382, Update the type-change handler in the NativeSelect to pass the current rule into ruleForType, and modify ruleForType to preserve the shared metricName and measure fields when switching between evaluation_threshold and evaluation_regression. Keep type-specific fields reset as appropriate while retaining the existing values for these shared fields.
615-636: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftTell the user which field blocks the save.
isValidGatereturns a single boolean. Line 321 disables the submit button when it isfalse. No field shows an error, and no input receivesaria-invalid. A user with an empty metric name or an out-of-range target sees a disabled button and no reason.Return per-field issues from the validator, mark the offending inputs with
aria-invalid, and render the messages with theFieldErrorslot from@lens/ui/components/field. Keep the submit button enabled and report the errors on submit, so keyboard and screen-reader users get the feedback.🤖 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 `@apps/web/src/modules/observability/components/quality-gates-view.tsx` around lines 615 - 636, Replace the boolean-only isValidGate validation with per-field issues that identify invalid gate and rule inputs, and keep the submit button enabled so submission surfaces those issues. In the quality-gates form, apply aria-invalid to each offending input and render its message through the FieldError slot from `@lens/ui/components/field`; ensure submit focuses or otherwise exposes the first error for keyboard and screen-reader users.apps/web/src/modules/observability/components/table-timestamp.test.tsx (1)
10-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winStrengthen the assertions in both tests.
Line 17 compares the rendered text to
formatTableTimestamp(value), the same function the component calls. The assertion passes for any formatting change, so it cannot detect a regression. Use a literal expected string and pin the time zone (for exampleprocess.env.TZor a Vitestenvoption) to make the format observable.The second test claims the component keeps an invalid timestamp visible, but it never renders
TableTimestamp. Render the component to cover that path.♻️ Proposed test change
it("keeps an invalid timestamp visible", () => { - expect(formatTableTimestamp("unknown")).toBe("unknown"); + render(<TableTimestamp value="unknown" />); + + expect(screen.getByTitle("unknown").textContent).toBe("unknown"); });🤖 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 `@apps/web/src/modules/observability/components/table-timestamp.test.tsx` around lines 10 - 22, Strengthen both tests around TableTimestamp: in the valid timestamp test, pin the test time zone and assert against a literal formatted string instead of calling formatTableTimestamp; in the invalid timestamp test, render TableTimestamp with "unknown" and assert the rendered output remains visible. Preserve the existing class assertions and use the component’s rendered element for both content checks.
🤖 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.
Inline comments:
In `@apps/web/src/components/centered-card.tsx`:
- Around line 40-47: Update the centered card rendering around props.icon so the
bordered icon wrapper is rendered only when an icon exists, and apply the
badge’s top margin only in that case. Preserve the badge rendering and secondary
variant for cards without an icon.
In `@apps/web/src/modules/observability/components/quality-gates-view.tsx`:
- Around line 76-117: Surface mutation errors in the matching create/edit and
delete dialogs by rendering state.createGate.error, state.updateGate.error, and
state.deleteGate.error near their respective actions, unless
use-evaluation-workspace already reports these errors globally. Preserve the
existing onSuccess close behavior and show actionable error text while each
mutation is pending or failed.
- Around line 267-276: In
apps/web/src/modules/observability/components/quality-gates-view.tsx#L267-L276,
add one shared number-input helper that maps an empty string to NaN and use it
for minimumCaseCount, rendering NaN as an empty input value. Apply the same
helper at `#L405-L414` for maxIncreasePercent and at `#L535-L543` inside
RuleValueField.onChange, also rendering its NaN value as empty; preserve
isValidGate’s finite-number validation so cleared fields cannot be saved.
- Around line 167-169: Update the Edit button in the quality-gate row rendering
to include an accessible label containing the gate name, matching the Delete
button’s `aria-label` pattern while preserving the visible “Edit” text. Update
the corresponding expectation in quality-gates-view.test.tsx to query the newly
specific accessible name.
---
Nitpick comments:
In `@apps/web/src/modules/observability/components/evaluation-runs-view.tsx`:
- Around line 247-261: Update the sort icon rendering in the Button around the
sort(field) handler: show ArrowUp or ArrowDown based on props.filters.order when
column is active, and use ArrowsDownUp for inactive columns. Preserve the
existing active-column styling and aria-label behavior.
In `@apps/web/src/modules/observability/components/quality-gates-view.tsx`:
- Around line 446-461: Update the measure change handler in the rule editor so
switching between “pass_rate” and “average_score” also resets the stored value
to the appropriate type default when crossing the percentage boundary,
preventing the existing value from being reinterpreted under the new measure.
- Around line 366-382: Update the type-change handler in the NativeSelect to
pass the current rule into ruleForType, and modify ruleForType to preserve the
shared metricName and measure fields when switching between evaluation_threshold
and evaluation_regression. Keep type-specific fields reset as appropriate while
retaining the existing values for these shared fields.
- Around line 615-636: Replace the boolean-only isValidGate validation with
per-field issues that identify invalid gate and rule inputs, and keep the submit
button enabled so submission surfaces those issues. In the quality-gates form,
apply aria-invalid to each offending input and render its message through the
FieldError slot from `@lens/ui/components/field`; ensure submit focuses or
otherwise exposes the first error for keyboard and screen-reader users.
In `@apps/web/src/modules/observability/components/table-timestamp.test.tsx`:
- Around line 10-22: Strengthen both tests around TableTimestamp: in the valid
timestamp test, pin the test time zone and assert against a literal formatted
string instead of calling formatTableTimestamp; in the invalid timestamp test,
render TableTimestamp with "unknown" and assert the rendered output remains
visible. Preserve the existing class assertions and use the component’s rendered
element for both content checks.
In `@packages/db/migrations/postgres/0007_remarkable_the_fallen.sql`:
- Line 1: Verify the rollout sequence for migration 0007 before applying the
destructive redaction_patterns drop: confirm no v0.1.0 API instances or other
runtime references still read or write redaction_patterns/redactionPatterns, and
ensure deployment stops or upgrades those instances before this migration runs;
otherwise use an expand/contract migration sequence.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 3227477a-9374-40d3-a86a-e5c5a6e2bf49
📒 Files selected for processing (41)
apps/api/src/modules/ingestion/logs-router.tsapps/api/src/modules/ingestion/router.tsapps/api/src/modules/projects/router.tsapps/api/src/modules/projects/services.tsapps/web/src/components/app-header.test.tsxapps/web/src/components/app-header.tsxapps/web/src/components/app-shell.test.tsxapps/web/src/components/centered-card.tsxapps/web/src/components/page.tsxapps/web/src/components/project-rail.test.tsxapps/web/src/modules/auth/components/auth-form.tsxapps/web/src/modules/auth/components/invitation-card.tsxapps/web/src/modules/observability/components/evaluation-compare-view.tsxapps/web/src/modules/observability/components/evaluation-run-detail-view.tsxapps/web/src/modules/observability/components/evaluation-runs-view.tsxapps/web/src/modules/observability/components/evaluation-status-badge.test.tsxapps/web/src/modules/observability/components/evaluation-status-badge.tsxapps/web/src/modules/observability/components/evaluations-view.tsxapps/web/src/modules/observability/components/quality-gates-view.test.tsxapps/web/src/modules/observability/components/quality-gates-view.tsxapps/web/src/modules/observability/components/status-badge.tsxapps/web/src/modules/observability/components/table-timestamp.test.tsxapps/web/src/modules/observability/components/table-timestamp.tsxapps/web/src/modules/observability/components/trace-data-table.test.tsxapps/web/src/modules/observability/utils/observability-view.tsxapps/web/src/modules/projects/components/connect-content.tsxapps/web/src/modules/projects/components/project-pages.test.tsxapps/web/src/modules/projects/components/project-settings.tsxapps/web/src/modules/projects/hooks/use-project-settings.tspackage.jsonpackages/contracts/src/projects.tspackages/contracts/test/contracts.test.tspackages/db/migrations/postgres/0007_remarkable_the_fallen.sqlpackages/db/migrations/postgres/meta/0007_snapshot.jsonpackages/db/migrations/postgres/meta/_journal.jsonpackages/db/src/schema.tspackages/db/src/seed-runner.tspackages/telemetry/src/normalization.tspackages/telemetry/src/normalize-evaluations.tspackages/telemetry/src/normalize-traces.tspackages/telemetry/test/telemetry.test.ts
💤 Files with no reviewable changes (9)
- apps/api/src/modules/ingestion/router.ts
- apps/api/src/modules/ingestion/logs-router.ts
- packages/db/src/schema.ts
- packages/contracts/src/projects.ts
- apps/api/src/modules/projects/router.ts
- packages/telemetry/src/normalization.ts
- packages/db/src/seed-runner.ts
- apps/api/src/modules/projects/services.ts
- apps/web/src/modules/projects/hooks/use-project-settings.ts
| ) : ( | ||
| <> | ||
| <span className="mx-auto flex size-10 items-center justify-center rounded-full border bg-background text-foreground"> | ||
| {props.icon} | ||
| </span> | ||
| <Badge className="mx-auto mt-2" variant="secondary"> | ||
| {props.eyebrow} | ||
| </Badge> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Do not render an empty icon container.
icon is optional, but the standard layout always renders its wrapper. A caller without icon shows an empty bordered circle above the badge. Render the wrapper only when props.icon exists. Remove the badge top margin when no icon exists.
Proposed fix
- <span className="mx-auto flex size-10 items-center justify-center rounded-full border bg-background text-foreground">
- {props.icon}
- </span>
- <Badge className="mx-auto mt-2" variant="secondary">
+ {props.icon ? (
+ <span className="mx-auto flex size-10 items-center justify-center rounded-full border bg-background text-foreground">
+ {props.icon}
+ </span>
+ ) : null}
+ <Badge className={cn("mx-auto", props.icon && "mt-2")} variant="secondary">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ) : ( | |
| <> | |
| <span className="mx-auto flex size-10 items-center justify-center rounded-full border bg-background text-foreground"> | |
| {props.icon} | |
| </span> | |
| <Badge className="mx-auto mt-2" variant="secondary"> | |
| {props.eyebrow} | |
| </Badge> | |
| ) : ( | |
| <> | |
| {props.icon ? ( | |
| <span className="mx-auto flex size-10 items-center justify-center rounded-full border bg-background text-foreground"> | |
| {props.icon} | |
| </span> | |
| ) : null} | |
| <Badge className={cn("mx-auto", props.icon && "mt-2")} variant="secondary"> | |
| {props.eyebrow} | |
| </Badge> |
🤖 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 `@apps/web/src/components/centered-card.tsx` around lines 40 - 47, Update the
centered card rendering around props.icon so the bordered icon wrapper is
rendered only when an icon exists, and apply the badge’s top margin only in that
case. Preserve the badge rendering and secondary variant for cards without an
icon.
| <GateDialog | ||
| item={editing} | ||
| saving={saving} | ||
| onClose={() => setEditing(null)} | ||
| onSave={(input) => { | ||
| if (editing === "new") | ||
| if (editing === "new") { | ||
| state.createGate.mutate(input, { onSuccess: () => setEditing(null) }); | ||
| else if (editing) | ||
| } else if (editing) { | ||
| state.updateGate.mutate( | ||
| { id: editing.id, input }, | ||
| { onSuccess: () => setEditing(null) }, | ||
| ); | ||
| } | ||
| }} | ||
| /> | ||
|
|
||
| <AlertDialog open={deleting !== null} onOpenChange={(open) => !open && setDeleting(null)}> | ||
| <AlertDialogContent> | ||
| <AlertDialogHeader> | ||
| <AlertDialogTitle>Delete this quality gate?</AlertDialogTitle> | ||
| <AlertDialogDescription> | ||
| {deleting | ||
| ? `“${deleting.name}” will no longer be available when comparing evaluation runs.` | ||
| : "This gate will no longer be available when comparing evaluation runs."} | ||
| </AlertDialogDescription> | ||
| </AlertDialogHeader> | ||
| <AlertDialogFooter> | ||
| <AlertDialogCancel>Cancel</AlertDialogCancel> | ||
| <AlertDialogAction | ||
| variant="destructive" | ||
| disabled={state.deleteGate.isPending} | ||
| onClick={() => { | ||
| if (deleting) { | ||
| state.deleteGate.mutate(deleting.id, { onSuccess: () => setDeleting(null) }); | ||
| } | ||
| }} | ||
| > | ||
| Delete gate | ||
| </AlertDialogAction> | ||
| </AlertDialogFooter> | ||
| </AlertDialogContent> | ||
| </AlertDialog> |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Surface create, update, and delete errors.
Line 61 renders only state.gates.error. The gate dialog and the delete dialog close only in onSuccess. If a mutation fails, the dialog stays open, the button re-enables, and the user gets no reason for the failure. Render state.createGate.error, state.updateGate.error, and state.deleteGate.error in the matching dialog, unless a global error handler already reports them.
#!/bin/bash
# Description: Check whether mutations in the evaluation workspace hook report errors globally (toast, onError).
fd -t f 'use-evaluation-workspace.ts' | xargs rg -n -C4 'onError|toast|useMutation'🤖 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 `@apps/web/src/modules/observability/components/quality-gates-view.tsx` around
lines 76 - 117, Surface mutation errors in the matching create/edit and delete
dialogs by rendering state.createGate.error, state.updateGate.error, and
state.deleteGate.error near their respective actions, unless
use-evaluation-workspace already reports these errors globally. Preserve the
existing onSuccess close behavior and show actionable error text while each
mutation is pending or failed.
| <Button size="sm" variant="ghost" onClick={() => props.onEdit(gate)}> | ||
| <PencilSimple /> Edit | ||
| </Button> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Give each Edit button a unique accessible name.
Every row renders an Edit button with the accessible name "Edit". With more than one gate, screen-reader users cannot tell the rows apart. The Delete button already uses aria-label={\Delete ${gate.name}`}`. Apply the same pattern to Edit.
♿ Proposed fix
- <Button size="sm" variant="ghost" onClick={() => props.onEdit(gate)}>
+ <Button
+ aria-label={`Edit ${gate.name}`}
+ size="sm"
+ variant="ghost"
+ onClick={() => props.onEdit(gate)}
+ >
<PencilSimple /> Edit
</Button>This changes the accessible name, so update quality-gates-view.test.tsx line 16 accordingly.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Button size="sm" variant="ghost" onClick={() => props.onEdit(gate)}> | |
| <PencilSimple /> Edit | |
| </Button> | |
| <Button | |
| aria-label={`Edit ${gate.name}`} | |
| size="sm" | |
| variant="ghost" | |
| onClick={() => props.onEdit(gate)} | |
| > | |
| <PencilSimple /> Edit | |
| </Button> |
🤖 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 `@apps/web/src/modules/observability/components/quality-gates-view.tsx` around
lines 167 - 169, Update the Edit button in the quality-gate row rendering to
include an accessible label containing the gate name, matching the Delete
button’s `aria-label` pattern while preserving the visible “Edit” text. Update
the corresponding expectation in quality-gates-view.test.tsx to query the newly
specific accessible name.
| <Input | ||
| id="gate-minimum-cases" | ||
| min={1} | ||
| max={1_000_000} | ||
| type="number" | ||
| value={value.minimumCaseCount} | ||
| onChange={(event) => | ||
| setValue({ ...value, minimumCaseCount: Number(event.target.value) }) | ||
| } | ||
| /> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Number inputs turn a cleared field into 0. All three numeric inputs call Number(event.target.value). A cleared input[type=number] reports "", and Number("") returns 0, so the draft receives 0 and the field re-renders as "0". The user must delete the "0" before typing. For maxIncreasePercent and the rule value, 0 also passes isValidGate, so a cleared field can be saved as a real 0 threshold.
Add one shared helper and use it at each site, for example const numberInput = (raw: string) => (raw === "" ? Number.NaN : Number(raw));. isValidGate already rejects NaN through Number.isFinite, so the save stays blocked while the field is empty. Render the field with Number.isNaN(value) ? "" : value so the input stays empty.
apps/web/src/modules/observability/components/quality-gates-view.tsx#L267-L276: apply the helper tominimumCaseCountand render an empty string when the value isNaN.apps/web/src/modules/observability/components/quality-gates-view.tsx#L405-L414: apply the helper tomaxIncreasePercent;isValidGatethen rejects the empty field instead of accepting0.apps/web/src/modules/observability/components/quality-gates-view.tsx#L535-L543: apply the helper insideRuleValueField.onChangeand guard thevalueprop forNaN.
📍 Affects 1 file
apps/web/src/modules/observability/components/quality-gates-view.tsx#L267-L276(this comment)apps/web/src/modules/observability/components/quality-gates-view.tsx#L405-L414apps/web/src/modules/observability/components/quality-gates-view.tsx#L535-L543
🤖 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 `@apps/web/src/modules/observability/components/quality-gates-view.tsx` around
lines 267 - 276, In
apps/web/src/modules/observability/components/quality-gates-view.tsx#L267-L276,
add one shared number-input helper that maps an empty string to NaN and use it
for minimumCaseCount, rendering NaN as an empty input value. Apply the same
helper at `#L405-L414` for maxIncreasePercent and at `#L535-L543` inside
RuleValueField.onChange, also rendering its NaN value as empty; preserve
isValidGate’s finite-number validation so cleared fields cannot be saved.
What changed
0.1.0to0.2.0Why
Several evaluation and project-management screens used different visual patterns from Traces and Sessions. Quality-gate rules also remounted while typing because their React keys were derived from editable JSON, making the modal difficult to control. Redaction controls were no longer wanted in the product.
Impact
The primary observability workflows now share compact timestamps, semantic status badges, table behavior, and clearer responsive layouts. Authentication and project setup are simpler. Existing deployments will apply migration
0007_remarkable_the_fallen.sql, which removes the obsolete project redaction settings column.Validation
pnpm checkpnpm typecheckpnpm testdocker-compose.dev.ymlSummary by CodeRabbit
New Features
Enhancements
Changes