feat(lab): CL-05 compatibility matrix UI - #1384
Conversation
Add GUI compatibility matrix page with lab API integration, i18n, tests, and docs.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdded a read-only Compatibility Matrix tab to the Models workspace. The change includes legacy hash routing, typed API parsing and loading, verdict and detail views, localization, responsive styling, and comprehensive validation. ChangesCompatibility Lab UI
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant Models
participant CompatibilityMatrix
participant LabAPI
User->>Models: Select Compatibility tab
Models->>CompatibilityMatrix: Mount active panel
CompatibilityMatrix->>LabAPI: GET status, subjects, and verdicts
LabAPI-->>CompatibilityMatrix: Return compatibility data
CompatibilityMatrix-->>User: Render matrix and verdicts
User->>CompatibilityMatrix: Select verdict
CompatibilityMatrix->>LabAPI: GET subject, observations, events, and artifacts
LabAPI-->>CompatibilityMatrix: Return detail data
CompatibilityMatrix-->>User: Render detail pane
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 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 |
|
✅ Deterministic PR hygiene checks passed. |
✅ READY
Hygiene✅ Deterministic PR hygiene checks passed. |
Relocate the read-only compatibility matrix from standalone #lab sidebar route to Models → Compatibility (#models/compatibility) with legacy #lab redirect, server-side verdict filters, pagination, and detail pane.
There was a problem hiding this comment.
Actionable comments posted: 17
🤖 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 `@devlog/_plan/260807_compatibility_lab/005_cl05_compatibility_matrix_ui.md`:
- Line 22: Update the component/layout test reference in the compatibility
matrix plan to include both compatibility-lab.test.tsx and
compatibility-matrix-layout.test.ts, or broaden it to gui/tests/ so the complete
test coverage is identified.
In `@gui/src/app-routing.ts`:
- Around line 135-137: Update readPageFromHash to include the legacy "lab" hash
in the same destination-page arm as "combos" and "routing", returning "models"
before resolver replacement. Add a neighboring assertion in
tests/models-workspace-tabs.test.ts verifying readPageFromHash("`#lab`") returns
"models".
In `@gui/src/i18n/ja.ts`:
- Around line 1934-1980: Translate every lab.* value from lab.title through
lab.layer.task_effectiveness, preserving all keys, in gui/src/i18n/ja.ts lines
1934-1980, gui/src/i18n/ko.ts lines 1934-1981, gui/src/i18n/ru.ts lines
1936-1982, gui/src/i18n/tr.ts lines 1936-1982, and gui/src/i18n/zh.ts lines
1934-1980. Use Japanese, Korean, Russian, Turkish, and Chinese respectively,
including status, filters, matrix headers, verdict labels, and detail-pane text.
In `@gui/src/pages/compatibility-matrix-api.ts`:
- Around line 204-228: Update fetchVerdictDetail to avoid unbounded event and
artifact request concurrency by processing those optional requests through a
bounded concurrency limit. Replace the all-or-nothing handling for event and
artifact fetches with Promise.allSettled, retaining successful results while
filtering rejected or null responses; keep subject and observations as the
required requests and preserve the existing event/artifact result separation.
- Around line 87-99: Update fetchAllSubjects to track previously requested
cursors and stop when the server returns a cursor already seen, while preserving
the existing subject aggregation. Add a hard maximum page count to bound
requests even when cursors keep changing, and ensure the loop exits cleanly when
either guard is reached or pagination completes.
In `@gui/src/pages/compatibility-matrix-shared.ts`:
- Around line 283-291: Keep the subject control as free-text search by removing
subjectId mapping from verdictQueryFromFilters and applying the existing
filterVerdicts substring matching to the loaded verdict rows in
CompatibilityMatrix. Preserve server-side filtering for layer, verdict, and
suiteId, and ensure the displayed state reflects the filtered API response.
- Around line 173-182: Update parseVerdictDto to validate projectionKey and asOf
as strings, and require scenarioManifestDigests, contributingEventIds, and
contradictingEventIds to be arrays before returning the cast VerdictDto.
Preserve the existing enum and scalar validation, returning null for any missing
or invalid field so successful parsing guarantees the complete verdict shape.
In `@gui/src/pages/CompatibilityMatrix.tsx`:
- Around line 222-235: The closed-value artifact status and unknown subject-kind
text bypass localization. In gui/src/pages/CompatibilityMatrix.tsx:222-235, add
an ARTIFACT_STATUS_LABEL map keyed by ArtifactMetadataDto["status"] and render
the translated label via t; in
gui/src/pages/compatibility-matrix-shared.ts:301-322, have buildMatrixRows
return an empty sentinel instead of the literal "unknown", then localize that
sentinel at the CompatibilityMatrix render site. Add the required
lab.subjectKindUnknown and artifact-status locale keys to all seven locales: de,
en, ja, ko, ru, tr, and zh.
- Around line 330-346: Keep verdict selection separate from matrix query state
by removing the suiteId mutation from selectVerdict. Selecting a verdict should
only update selection/detail state and must not alter filters or pagination;
preserve updateFilters for intentional user-driven query changes.
- Around line 62-90: Rename the VerdictBadge prop suiteId to a name representing
the displayed suite identifier, update its title and suite-label rendering to
use the renamed prop, and adjust both call sites. Keep the matrix cell passing
row.suiteId, while changing the verdict table call site to pass its actual suite
identifier rather than verdict.suiteVersion.
- Around line 505-517: Update the interactive rows rendered by allVerdicts.map
in CompatibilityMatrix to expose their control semantics and visual selection
state through ARIA, using the lab.selectVerdict localization key for each row’s
accessible label and marking the selected verdict accordingly. Add
lab.selectVerdict to every locale file, while preserving the existing click and
keyboard activation behavior.
- Around line 267-288: Debounce only subject text-filter updates before they
reach updateFilters, so typing does not recompute queryKey/resourceKey or
trigger fetchLabPageData on every keystroke. Preserve immediate updates for the
Select-based filters and ensure the debounced update still resets pagination
state through updateFilters.
- Around line 311-326: Update loadMore to track the request’s active query key
and only apply page.verdicts, nextCursor, and hasMore when that key still
matches the current filters. Store the AbortController in a ref, abort any
in-flight request when filters change or the component unmounts, and preserve
loadingMore cleanup without updating unmounted state.
In `@gui/tests/compatibility-lab.test.tsx`:
- Around line 455-464: Add a focused regression test near the existing
compatibility matrix tests for verdict selection: capture the requests returned
by installLabFetch, record the initial .lab-matrix row count, click a detail
verdict row, then assert no request includes suiteId= and the matrix row count
is unchanged.
- Around line 466-473: Update the “16. inactive panel suppresses loads” test to
use the existing timer-pumping approach from waitFor, advancing real timers and
allowing pending microtasks to settle before checking tracked requests. Keep the
assertion focused on ensuring no “/api/lab/” request occurs while
renderMatrix(false) leaves the panel inactive.
- Around line 438-447: Rename the detail-pane heading used for the merged events
list from “Contributing events” to a neutral “Evidence events” label. Update the
locale key/value consumed by CompatibilityMatrix and change the “13. detail flow
on selection” assertion to expect the new heading, preserving the existing
combined contributing/contradicting event list.
In `@tests/models-workspace-tabs.test.ts`:
- Around line 139-146: Extend the “legacy lab hash” tests to cover delimiter
awareness, nested legacy hashes, and page resolution. Add assertions that “labs”
resolves to the catalog rather than the legacy lab route, that “lab/anything”
maps to the models compatibility tab through both resolveAppHashChange and
readModelsTab, and that readPageFromHash recognizes both bare and nested lab
hashes as the models page.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 78ecb4ee-e277-4df7-81b7-34568b71456c
📒 Files selected for processing (23)
devlog/_plan/260807_compatibility_lab/001_pr_stack_status.mddevlog/_plan/260807_compatibility_lab/005_cl05_compatibility_matrix_ui.mdgui/src/app-routing.tsgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/tr.tsgui/src/i18n/zh.tsgui/src/pages/CompatibilityMatrix.tsxgui/src/pages/Models.tsxgui/src/pages/compatibility-matrix-api.tsgui/src/pages/compatibility-matrix-shared.tsgui/src/pages/models-tab-strip.tsxgui/src/pages/models-tab.tsgui/src/styles-compatibility-matrix.cssgui/src/styles.cssgui/tests/compatibility-lab.test.tsxgui/tests/compatibility-matrix-layout.test.tsgui/tests/models-workspace-panels.test.tsxstructure/09_compatibility-lab.mdtests/models-workspace-tabs.test.ts
| "lab.title": "Compatibility Lab", | ||
| "lab.subtitle": "Read-only compatibility verdict matrix from lab projection evidence.", | ||
| "lab.loadFailed": "Could not load compatibility lab data", | ||
| "lab.projectionUnavailable": "Lab projection is not available. Run conformance or live probes first.", | ||
| "lab.projectionIncompatible": "Lab projection schema is incompatible. Rebuild the projection.", | ||
| "lab.statusTitle": "Projection status", | ||
| "lab.matrixTitle": "Compatibility matrix", | ||
| "lab.verdictsTitle": "Verdict records", | ||
| "lab.filter.layer": "Evidence layer", | ||
| "lab.filter.verdict": "Verdict", | ||
| "lab.filter.subject": "Subject ID", | ||
| "lab.filter.all": "All", | ||
| "lab.col.subject": "Subject", | ||
| "lab.col.layer": "Layer", | ||
| "lab.col.suite": "Suite", | ||
| "lab.col.verdict": "Verdict", | ||
| "lab.col.asOf": "As of", | ||
| "lab.col.protocol": "Protocol conformance", | ||
| "lab.col.live": "Live route compatibility", | ||
| "lab.col.task": "Task effectiveness", | ||
| "lab.empty": "No compatibility verdicts in the projection yet.", | ||
| "lab.subjectKind": "Kind", | ||
| "lab.observationCount": "Observations", | ||
| "lab.eventCount": "Events", | ||
| "lab.verdictCount": "Verdicts", | ||
| "lab.subjectCount": "Subjects", | ||
| "lab.builtAt": "Built", | ||
| "lab.loading": "Loading compatibility evidence…", | ||
| "lab.loadMore": "Load more", | ||
| "lab.detailTitle": "Verdict detail", | ||
| "lab.detailClose": "Close", | ||
| "lab.detailSubject": "Subject", | ||
| "lab.detailObservations": "Observations", | ||
| "lab.detailEvents": "Contributing events", | ||
| "lab.detailArtifacts": "Artifact metadata", | ||
| "lab.detailLoadFailed": "Could not load verdict detail", | ||
| "lab.refresh": "Refresh", | ||
| "lab.verdict.UNKNOWN": "Unknown", | ||
| "lab.verdict.CLAIMED": "Claimed", | ||
| "lab.verdict.PROBED": "Probed", | ||
| "lab.verdict.VERIFIED": "Verified", | ||
| "lab.verdict.DEGRADED": "Degraded", | ||
| "lab.verdict.BLOCKED": "Blocked", | ||
| "lab.verdict.UNSUPPORTED": "Unsupported", | ||
| "lab.layer.protocol_conformance": "Protocol conformance", | ||
| "lab.layer.live_route_compatibility": "Live route compatibility", | ||
| "lab.layer.task_effectiveness": "Task effectiveness", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Root cause: the new CL-05 "lab.*" i18n block was copied from en.ts without translation into five of the seven locale catalogs. gui/src/i18n/de.ts (Lines 1913-1959) shows the correct pattern — every "lab.*" key properly translated into German. The same block in the following five files still holds the exact English source strings, so Japanese, Korean, Russian, Turkish, and Chinese users of the new Compatibility Matrix tab will see English text for the entire feature (status card, filters, matrix headers, verdict badges, and detail pane):
gui/src/i18n/ja.ts#L1934-L1980: translate all keys from"lab.title"through"lab.layer.task_effectiveness"into Japanese.gui/src/i18n/ko.ts#L1934-L1981: translate the same key range into Korean.gui/src/i18n/ru.ts#L1936-L1982: translate the same key range into Russian.gui/src/i18n/tr.ts#L1936-L1982: translate the same key range into Turkish.gui/src/i18n/zh.ts#L1934-L1980: translate the same key range into Chinese.
Because Record<TKey, string> in each file only enforces key-set parity with en.ts, TypeScript compiles cleanly even with untranslated placeholder values, so this gap will not be caught until a non-German, non-English user opens the tab.
📍 Affects 5 files
gui/src/i18n/ja.ts#L1934-L1980(this comment)gui/src/i18n/ko.ts#L1934-L1981gui/src/i18n/ru.ts#L1936-L1982gui/src/i18n/tr.ts#L1936-L1982gui/src/i18n/zh.ts#L1934-L1980
🤖 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 `@gui/src/i18n/ja.ts` around lines 1934 - 1980, Translate every lab.* value
from lab.title through lab.layer.task_effectiveness, preserving all keys, in
gui/src/i18n/ja.ts lines 1934-1980, gui/src/i18n/ko.ts lines 1934-1981,
gui/src/i18n/ru.ts lines 1936-1982, gui/src/i18n/tr.ts lines 1936-1982, and
gui/src/i18n/zh.ts lines 1934-1980. Use Japanese, Korean, Russian, Turkish, and
Chinese respectively, including status, filters, matrix headers, verdict labels,
and detail-pane text.
| test("13. detail flow on selection", async () => { | ||
| installLabFetch(); | ||
| const { root, container } = await renderMatrix(); | ||
| await waitFor(() => container.querySelector(".lab-detail-table tbody tr") !== null); | ||
| const row = container.querySelector(".lab-detail-table tbody tr") as HTMLTableRowElement; | ||
| await act(async () => { row.click(); }); | ||
| await waitFor(() => container.querySelector(".lab-detail-pane") !== null); | ||
| expect(container.textContent).toContain("Contributing events"); | ||
| await act(async () => root.unmount()); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm the English value behind lab.detailEvents and how the events list is built.
set -uo pipefail
echo "== lab.detailEvents across locales =="
fd -e ts . gui/src/i18n --exec rg -n 'lab\.detailEvents|"lab\.detail' {}
echo
echo "== events list construction =="
rg -n -C4 'contributingEventIds|contradictingEventIds' gui/src/pages/Repository: lidge-jun/opencodex
Length of output: 4082
🏁 Script executed:
#!/bin/bash
set -uo pipefail
echo "== CompatibilityMatrix outline =="
ast-grep outline gui/src/pages/CompatibilityMatrix.tsx --view compact || true
echo
echo "== DetailPane section =="
sed -n '160,240p' gui/src/pages/CompatibilityMatrix.tsx
echo
echo "== fetchVerdictDetail return shape =="
sed -n '190,235p' gui/src/pages/compatibility-matrix-api.ts
echo
echo "== i18n keys around lab.detailEvents =="
sed -n '1950,1975p' gui/src/i18n/en.ts
sed -n '1960,1980p' gui/src/i18n/es.ts || true
sed -n '1940,1970p' gui/src/i18n/fr.ts || true
sed -n '1960,1985p' gui/src/i18n/zh.ts || trueRepository: lidge-jun/opencodex
Length of output: 6899
Rename the events heading to match mixed contributing/contradicting evidence.
gui/src/pages/compatibility-matrix-api.ts:209 builds events from both verdict.contributingEventIds and verdict.contradictingEventIds, but gui/src/pages/CompatibilityMatrix.tsx:221 renders the merged list under t("lab.detailEvents"), and the English locale value is "Contributing events". If contradictingEventIds contains events, the pane labels contradicting events as contributing. Use a neutral label such as "Evidence events" in the locale files, or split the list into contributing and contradicting sections and update the test assertion.
🤖 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 `@gui/tests/compatibility-lab.test.tsx` around lines 438 - 447, Rename the
detail-pane heading used for the merged events list from “Contributing events”
to a neutral “Evidence events” label. Update the locale key/value consumed by
CompatibilityMatrix and change the “13. detail flow on selection” assertion to
expect the new heading, preserving the existing combined
contributing/contradicting event list.
Capture real Models → Compatibility UI states for PR lidge-jun#1384 review and the GUI screenshot gate.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@devlog/_plan/260807_compatibility_lab/005_cl05_compatibility_matrix_ui.md`:
- Around line 55-56: Update the recorded GUI validation command to include
tests/compatibility-matrix-layout.test.ts alongside the existing test files, and
revise the reported passing test count accordingly; alternatively, explicitly
document that the layout test was validated separately.
In `@gui/src/pages/compatibility-matrix-api.ts`:
- Around line 113-131: Update collectPages so reaching MAX_PAGES is treated as a
successful, truncated result rather than throwing LabDataContractError; retain
the existing cursor-repeat and invalid-cursor guard as the contract-violation
path. Preserve all rows collected before the cap and return them, allowing
fetchAllSubjects and fetchLabPageData to render partial data.
In `@gui/src/pages/CompatibilityMatrix.tsx`:
- Around line 319-325: Update reportedCount in the useMemo around reportedCount
so surface.data.status.verdictCount is used only when no layer, verdict,
subjectId, or suiteId filter is active; when any filter is selected, return the
filtered verdict rows count from surface.data.verdicts plus
validExtraPage.verdicts. Preserve the existing inactive/projection-unavailable
null behavior and update dependencies if the filter state is not already
covered.
In `@gui/tests/compatibility-lab.test.tsx`:
- Around line 392-398: Update the waitFor predicate in the “detail flow on
selection uses a neutral evidence heading” test to wait directly for the detail
pane to contain “Evidence events” instead of checking for the generic “Loading…”
text. Keep the existing detail-pane presence check and assert the same heading
afterward.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 98a65368-94be-4cea-b75b-e1e4b27c677a
⛔ Files ignored due to path filters (5)
devlog/_plan/260807_compatibility_lab/screenshots/cl-05/01-matrix-populated-dark.pngis excluded by!**/*.pngdevlog/_plan/260807_compatibility_lab/screenshots/cl-05/02-projection-unavailable-dark.pngis excluded by!**/*.pngdevlog/_plan/260807_compatibility_lab/screenshots/cl-05/03-projection-empty-dark.pngis excluded by!**/*.pngdevlog/_plan/260807_compatibility_lab/screenshots/cl-05/04-verdict-detail-dark.pngis excluded by!**/*.pngdevlog/_plan/260807_compatibility_lab/screenshots/cl-05/05-matrix-narrow-dark.pngis excluded by!**/*.png
📒 Files selected for processing (12)
devlog/_plan/260807_compatibility_lab/005_cl05_compatibility_matrix_ui.mdgui/src/app-routing.tsgui/src/i18n/catalogs.tsgui/src/i18n/lab-translations.tsgui/src/pages/CompatibilityMatrix.tsxgui/src/pages/compatibility-matrix-api.tsgui/src/pages/compatibility-matrix-shared.tsgui/src/styles-compatibility-matrix.cssgui/tests/compatibility-lab-i18n.test.tsgui/tests/compatibility-lab.test.tsxgui/tests/compatibility-matrix-layout.test.tstests/models-workspace-tabs.test.ts
| const reportedCount = useMemo(() => { | ||
| if (!active || !surface.data?.status.projectionAvailable) return null; | ||
| const total = surface.data.status.verdictCount; | ||
| return typeof total === "number" | ||
| ? total | ||
| : surface.data.verdicts.length + (validExtraPage?.verdicts.length ?? 0); | ||
| }, [active, surface.data, validExtraPage]); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
The reported count ignores the active filters, so the tab badge contradicts the table.
reportedCount prefers surface.data.status.verdictCount. That value comes from /api/lab/status, which fetchLabStatus requests without any query parameters (gui/src/pages/compatibility-matrix-api.ts Line 79). The verdict rows, by contrast, come from /api/lab/verdicts with layer, verdict, subjectId, and suiteId applied (gui/src/pages/compatibility-matrix-api.ts Line 93-98).
Failure mode: a user selects one subject and one layer. The table shows 3 verdicts. onCountChange still reports the projection-wide total, so the Compatibility tab badge shows the unfiltered number. The two numbers on screen disagree, and nothing on the tab strip explains why.
The fallback branch at Line 324 already computes the correct filtered length. Use the status total only while no filter is active.
🔢 Proposed fix to align the reported count with the rendered rows
const reportedCount = useMemo(() => {
if (!active || !surface.data?.status.projectionAvailable) return null;
+ const loaded = surface.data.verdicts.length + (validExtraPage?.verdicts.length ?? 0);
+ // The status total is projection-wide. It only describes the table while the query is unfiltered.
+ if (Object.keys(queryFilters).length > 0) return loaded;
const total = surface.data.status.verdictCount;
- return typeof total === "number"
- ? total
- : surface.data.verdicts.length + (validExtraPage?.verdicts.length ?? 0);
- }, [active, surface.data, validExtraPage]);
+ return typeof total === "number" ? total : loaded;
+ }, [active, queryFilters, surface.data, validExtraPage]);Note that the unfiltered branch still under-reports nothing and over-reports nothing, because verdictCount and the unfiltered verdict list describe the same set.
As per path instructions for gui/**: "Check that GUI state changes stay consistent with the management API responses".
📝 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.
| const reportedCount = useMemo(() => { | |
| if (!active || !surface.data?.status.projectionAvailable) return null; | |
| const total = surface.data.status.verdictCount; | |
| return typeof total === "number" | |
| ? total | |
| : surface.data.verdicts.length + (validExtraPage?.verdicts.length ?? 0); | |
| }, [active, surface.data, validExtraPage]); | |
| const reportedCount = useMemo(() => { | |
| if (!active || !surface.data?.status.projectionAvailable) return null; | |
| const loaded = surface.data.verdicts.length + (validExtraPage?.verdicts.length ?? 0); | |
| // The status total is projection-wide. It only describes the table while the query is unfiltered. | |
| if (Object.keys(queryFilters).length > 0) return loaded; | |
| const total = surface.data.status.verdictCount; | |
| return typeof total === "number" ? total : loaded; | |
| }, [active, queryFilters, surface.data, validExtraPage]); |
🤖 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 `@gui/src/pages/CompatibilityMatrix.tsx` around lines 319 - 325, Update
reportedCount in the useMemo around reportedCount so
surface.data.status.verdictCount is used only when no layer, verdict, subjectId,
or suiteId filter is active; when any filter is selected, return the filtered
verdict rows count from surface.data.verdicts plus validExtraPage.verdicts.
Preserve the existing inactive/projection-unavailable null behavior and update
dependencies if the filter state is not already covered.
Source: Path instructions
| test("13. detail flow on selection uses a neutral evidence heading", async () => { | ||
| installLabFetch(); | ||
| const { root, container } = await renderMatrix(); | ||
| await waitFor(() => detailButton(container) !== null); | ||
| await act(async () => { detailButton(container).click(); }); | ||
| await waitFor(() => container.querySelector(".lab-detail-pane") !== null && !container.textContent?.includes("Loading…")); | ||
| expect(container.textContent).toContain("Evidence events"); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Wait for the loaded detail heading directly.
Line 397 checks for Loading…. The Compatibility Lab loader is Loading compatibility evidence… in gui/src/i18n/lab-translations.ts Line 40. The predicate can pass while loading content is still present. This can make the next assertion flaky.
Wait for Evidence events in the detail pane instead.
Proposed fix
- await waitFor(() => container.querySelector(".lab-detail-pane") !== null && !container.textContent?.includes("Loading…"));
+ await waitFor(() => container.querySelector(".lab-detail-pane")?.textContent?.includes("Evidence events") ?? false);📝 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.
| test("13. detail flow on selection uses a neutral evidence heading", async () => { | |
| installLabFetch(); | |
| const { root, container } = await renderMatrix(); | |
| await waitFor(() => detailButton(container) !== null); | |
| await act(async () => { detailButton(container).click(); }); | |
| await waitFor(() => container.querySelector(".lab-detail-pane") !== null && !container.textContent?.includes("Loading…")); | |
| expect(container.textContent).toContain("Evidence events"); | |
| test("13. detail flow on selection uses a neutral evidence heading", async () => { | |
| installLabFetch(); | |
| const { root, container } = await renderMatrix(); | |
| await waitFor(() => detailButton(container) !== null); | |
| await act(async () => { detailButton(container).click(); }); | |
| await waitFor(() => container.querySelector(".lab-detail-pane")?.textContent?.includes("Evidence events") ?? false); | |
| expect(container.textContent).toContain("Evidence events"); |
🤖 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 `@gui/tests/compatibility-lab.test.tsx` around lines 392 - 398, Update the
waitFor predicate in the “detail flow on selection uses a neutral evidence
heading” test to wait directly for the detail pane to contain “Evidence events”
instead of checking for the generic “Loading…” text. Keep the existing
detail-pane presence check and assert the same heading afterward.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@gui/tests/compatibility-pagination-cap.test.ts`:
- Around line 9-16: Strengthen the compatibility pagination cap test around the
fetch mock and its assertions by verifying cursor progression, not only request
counts and result truncation. Assert that the collected subject IDs follow the
expected subject-0 through subject-199 sequence, or record and validate the
requested cursor sequence, while preserving the existing cap behavior.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 75f0aace-1e19-4101-bd0b-e001f6351306
📒 Files selected for processing (4)
devlog/_plan/260807_compatibility_lab/005_cl05_compatibility_matrix_ui.mdgui/src/pages/compatibility-matrix-api.tsgui/tests/compatibility-pagination-cap.test.tstests/cli-headless-parity.test.ts
Summary
#models/compatibility(not a standalone sidebar page).#labredirects to#models/compatibility; matrix uses server-side verdict filters, paginated load-more, and a read-only detail pane.GUI screenshots
Captured against a local opencodex instance on
#models/compatibility(dark theme).projectionAvailable: false)Test plan
bun x tsc --noEmitbun test tests/lab-read-surfaces.test.ts tests/models-workspace-tabs.test.tscd gui && bun test tests/compatibility-lab.test.tsx tests/models-workspace-panels.test.tsxbun run lint:gui && bun run doctor:gui && bun run build:gui && bun run privacy:scanPlacement
#models/compatibility(Models workspace fourth tab)#lab→#models/compatibilityviaresolveAppHashChangeSummary by CodeRabbit