feat(gui): add routing profiles and why-this-route views - #1018
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdded routing-profile documentation and a GUI workspace. The workspace loads profiles and analytics, supports dry-run evaluation, displays candidate scores, and shows route-decision details in logs. Localization, navigation, tests, parity mappings, CI matrix checks, and acceptance records were added. ChangesRouting Intelligence
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Reviewer
participant RoutingProfiles
participant RoutingProfilesAPI
participant RoutingAnalyticsAPI
participant DryRunAPI
Reviewer->>RoutingProfiles: open routing page
RoutingProfiles->>RoutingProfilesAPI: request profiles
RoutingProfiles->>RoutingAnalyticsAPI: request analytics
Reviewer->>RoutingProfiles: submit dry-run evidence
RoutingProfiles->>DryRunAPI: post profile and evidence
DryRunAPI-->>RoutingProfiles: return candidates and scores
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 |
6629a6f to
3faacd4
Compare
|
✅ PR quality gates passed This pull request now targets The title was left unchanged. Its existing draft status has been preserved. |
|
✅ Deterministic PR hygiene checks passed. |
There was a problem hiding this comment.
💡 Codex Review
This says an unknown policy/<id> fails closed, but the router explicitly falls through to normal provider/default resolution when no configured profile exists (src/router.ts:496-501). With a default provider configured, a request for policy/missing can be sent upstream as that literal model id instead of failing, so this line should either match the runtime or the runtime should be changed to fail closed.
AGENTS.md reference: docs-site/AGENTS.md:L8-L10
ℹ️ 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".
2fcbc7b to
2fd66d3
Compare
There was a problem hiding this comment.
Actionable comments posted: 8
🤖 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/260804_router_intelligence/001_pr_stack_status.md`:
- Around line 51-52: Synchronize the RI-09 and RI-10 metadata from one final
stack snapshot. In
devlog/_plan/260804_router_intelligence/001_pr_stack_status.md lines 51-52,
update the RI-09 head to explicitly state whether
6e69a4a680f7c6ddf0ac5f4faf026a5cffbdd9c4 supersedes d887c1202 and record the
current RI-10 PR `#1018` state; in lines 263-276, use the same RI-09 base/head and
PR state, replacing the pending status with the recorded state.
In `@gui/src/pages/Logs.tsx`:
- Around line 947-951: Update the candidate formatting in the
routeDecision.candidates mapping to provide explicit fallback labels for missing
provider and model values, avoiding literal “undefined” output. Replace the
binary eligibility check with distinct handling for false, true, and undefined,
using a neutral marker for unknown eligibility while preserving the existing
failure and success markers.
- Around line 947-951: Update the candidate summary mapping in the
route-decision details to read each candidate’s optional exclusions and append
its available code values when present, especially for ineligible candidates.
Preserve the existing provider/model and eligibility markers, separator, and
empty fallback, and render codes directly without adding new i18n keys.
- Around line 142-148: Update validCachedLogs to validate the nested
routeDecision.candidates shape before accepting cached entries, ensuring it is
an array and each candidate has the expected object structure before the
rendering path uses .map. Discard any cache entry with malformed routeDecision
data while preserving valid cached logs and the existing rendering behavior.
In `@gui/src/pages/RoutingProfiles.tsx`:
- Around line 36-42: Update fmtMs and fmtRate in RoutingProfiles.tsx to use the
existing i18n locale mechanism: translate the unavailable marker, format
durations and percentages with the locale-aware number formatter, and add the
required unit/percent locale keys to the locale files. Replace the hardcoded
unavailable marker at the referenced display site with its locale key,
preserving the current rounding and output behavior.
- Around line 58-76: The load callback must accept only the latest reload
response: use a request-generation guard or AbortController for the fetches and
ignore superseded results. In load, clear analytics when analyticsRes is not OK,
and resolve selected from the accepted profiles response by matching the current
selection id against next, falling back to next[0] or null so removed profiles
cannot remain stale.
In `@gui/src/styles.css`:
- Around line 963-970: The routing table styles are duplicating the existing tbl
grammar under a new .table class instead of reusing the shared .tbl rules.
Remove the duplicated selectors from styles.css and update the RoutingProfiles
table markup to use the existing tbl class so future table styling changes stay
centralized and in sync.
In `@gui/tests/routing-profiles.test.tsx`:
- Line 24: Update the routing-profile fixture’s optimize objects to replace both
reliability keys with the supported health dimension, preserving the existing
score values and surrounding test data.
🪄 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: 9e81c7a0-cc69-4636-ad02-71531c8a7c09
⛔ Files ignored due to path filters (1)
docs-site/public/pr-screenshots/1018-routing-intelligence.pngis excluded by!**/*.png
📒 Files selected for processing (21)
devlog/_plan/260804_router_intelligence/001_pr_stack_status.mddocs-site/src/content/docs/ja/reference/configuration/routing.mddocs-site/src/content/docs/ko/reference/configuration/routing.mddocs-site/src/content/docs/reference/configuration/routing.mddocs-site/src/content/docs/ru/reference/configuration/routing.mddocs-site/src/content/docs/zh-cn/reference/configuration/routing.mdgui/src/App.tsxgui/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/zh.tsgui/src/icons.tsxgui/src/pages/Logs.tsxgui/src/pages/RoutingProfiles.tsxgui/src/styles.cssgui/tests/routing-profiles.test.tsxtests/cli-headless-parity.test.tstests/routing-intelligence-ui.test.ts
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
gui/src/pages/Logs.tsx (1)
959-965: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winUnknown route candidates still render as "healthy," and missing provider/model can still print the literal word "undefined."
Line 962 is:
`${candidate.provider}/${candidate.model}${candidate.eligible === false ? " ✗" : " ✓"}`Two failure modes remain in this exact line:
candidate.eligibleiseligible?: boolean(line 147). When it isundefined(eligibility was never determined), the ternary takes the" ✓"branch — the same branch aseligible === true. This contradicts the PR's own stated goal: "Unknown evidence renders neutrally rather than as healthy."candidate.providerandcandidate.modelare optional. If either is missing, the template literal stringifiesundefinedinto the label, so the dialog can show"undefined/gpt-5 ✓".A past review on this file already reported this exact bug at this exact line, with the same proposed fix, and that comment is marked "✅ Addressed in commits c9670fd to 0140e31." The code shown in this diff is identical to the code flagged as buggy in that earlier review — the referenced fix commits are not reflected here. Please re-verify against the latest commit on this branch; if the fix commits exist but are not in this snapshot, this comment can be dismissed once confirmed. If they are missing, apply the three-way eligibility check and explicit fallbacks:
🐛 Proposed fix
<span className="mono"> - {(detail.routeDecision.candidates ?? []).map(candidate => - `${candidate.provider}/${candidate.model}${candidate.eligible === false ? " ✗" : " ✓"}`, - ).join(" ") || "–"} + {(detail.routeDecision.candidates ?? []).map(candidate => { + const label = `${candidate.provider ?? "?"}/${candidate.model ?? "?"}`; + const mark = candidate.eligible === true ? " ✓" : candidate.eligible === false ? " ✗" : " ?"; + return `${label}${mark}`; + }).join(" ") || "–"} </span>🤖 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/Logs.tsx` around lines 959 - 965, Update the candidate label formatting in the route candidates map to treat eligible === true, eligible === false, and undefined as distinct states, rendering unknown eligibility neutrally rather than as healthy. Also provide explicit fallback text for missing candidate.provider and candidate.model so the label never contains the literal “undefined”; first verify whether the referenced fix commits are already present before changing the implementation.
🤖 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/260804_router_intelligence/001_pr_stack_status.md`:
- Around line 289-316: Update the fourth self-review finding to state that the
routing page uses the existing .tbl class and that .checkbox was added to
styles.css, removing the inaccurate .table claim. Also delete the duplicate
“Remaining Low findings: none” entry, preserving the single remaining entry.
In `@docs-site/src/content/docs/reference/configuration/routing.md`:
- Around line 195-208: Document the privacy boundary for routing history,
route-decision traces, analytics, and dry-run output: state that identifiers
such as apiKeyId are masked metadata and that responses exclude credentials,
prompt contents, raw upstream requests and responses, login diagnostics, and
database files. Add equivalent translated guidance in
docs-site/src/content/docs/ja/reference/configuration/routing.md:98-101,
docs-site/src/content/docs/ko/reference/configuration/routing.md:96-99,
docs-site/src/content/docs/ru/reference/configuration/routing.md:113-116, and
docs-site/src/content/docs/zh-cn/reference/configuration/routing.md:103-106,
alongside the English documentation at
docs-site/src/content/docs/reference/configuration/routing.md:195-208.
In `@gui/src/pages/RoutingProfiles.tsx`:
- Around line 133-169: Update the dry-run flow around runDryRun and clearDryRun
to invalidate superseded requests when the selected profile or evidence inputs
change, using a generation token or AbortController. Before applying the
response result, error, or final running state, verify the request is still
current so stale responses cannot overwrite the active state; add a regression
test covering a delayed response followed by a profile or evidence change.
---
Duplicate comments:
In `@gui/src/pages/Logs.tsx`:
- Around line 959-965: Update the candidate label formatting in the route
candidates map to treat eligible === true, eligible === false, and undefined as
distinct states, rendering unknown eligibility neutrally rather than as healthy.
Also provide explicit fallback text for missing candidate.provider and
candidate.model so the label never contains the literal “undefined”; first
verify whether the referenced fix commits are already present before changing
the implementation.
🪄 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: 3109cd12-7da1-4908-a95e-abc7f43047b7
⛔ Files ignored due to path filters (1)
docs-site/public/pr-screenshots/1018-routing-intelligence.pngis excluded by!**/*.png
📒 Files selected for processing (21)
devlog/_plan/260804_router_intelligence/001_pr_stack_status.mddocs-site/src/content/docs/ja/reference/configuration/routing.mddocs-site/src/content/docs/ko/reference/configuration/routing.mddocs-site/src/content/docs/reference/configuration/routing.mddocs-site/src/content/docs/ru/reference/configuration/routing.mddocs-site/src/content/docs/zh-cn/reference/configuration/routing.mdgui/src/App.tsxgui/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/zh.tsgui/src/icons.tsxgui/src/pages/Logs.tsxgui/src/pages/RoutingProfiles.tsxgui/src/styles.cssgui/tests/routing-profiles.test.tsxtests/cli-headless-parity.test.tstests/routing-intelligence-ui.test.ts
53f8da7 to
416ad2f
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/src/pages/Logs.tsx`:
- Around line 142-162: Strengthen validCachedRouteDecision to reject non-string
values in routeKind, profile.id, profile.revision, selected.provider, and
selected.model, while preserving valid optional and candidate handling. Apply
this validation to API results within loadLogs before returning or passing data
to writeSessionListCache, ensuring malformed route decisions are filtered or
rejected before Logs renders them.
In `@gui/tests/routing-profiles.test.tsx`:
- Around line 37-41: Update the PROFILE_REFRESHED fixture’s require object to
use the supported imageInput field instead of image, preserving the existing
boolean value and fixture structure.
🪄 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: 3c2d2253-e2ae-47c4-bbff-f1fcf30ce583
⛔ Files ignored due to path filters (1)
docs-site/public/pr-screenshots/1018-routing-intelligence.pngis excluded by!**/*.png
📒 Files selected for processing (22)
devlog/_plan/260804_router_intelligence/001_pr_stack_status.mddocs-site/src/content/docs/ja/reference/configuration/routing.mddocs-site/src/content/docs/ko/reference/configuration/routing.mddocs-site/src/content/docs/reference/configuration/routing.mddocs-site/src/content/docs/ru/reference/configuration/routing.mddocs-site/src/content/docs/zh-cn/reference/configuration/routing.mdgui/src/App.tsxgui/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/zh.tsgui/src/icons.tsxgui/src/pages/Logs.tsxgui/src/pages/RoutingProfiles.tsxgui/src/styles.cssgui/tests/routing-profiles.test.tsxtests/ci-workflows.test.tstests/cli-headless-parity.test.tstests/routing-intelligence-ui.test.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
gui/src/pages/Logs.tsx (1)
977-1015: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRender missing route fields with a neutral fallback.
When
profile.idexists butprofile.revisionis absent, Line 984 rendersundefined. Whenselected.providerexists butselected.modelis absent, Lines 989-990 renderprovider/undefined.validCachedRouteDecisionpermits these partial traces.Render missing values as
–, or omit the optional portion. Add regression tests for both partial trace shapes.Proposed fix
- <span className="mono">{detail.routeDecision.profile.id} ({detail.routeDecision.profile.revision})</span></> + <span className="mono"> + {detail.routeDecision.profile.id} ({detail.routeDecision.profile.revision || "–"}) + </span></> ... - {detail.routeDecision.selected.provider}/{detail.routeDecision.selected.model} + {detail.routeDecision.selected.provider}/{detail.routeDecision.selected.model || "–"}🤖 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/Logs.tsx` around lines 977 - 1015, Update the route detail rendering in the Logs component so missing profile.revision and selected.model values use the neutral “–” fallback (or omit the optional profile revision) instead of rendering undefined, while preserving existing display for present values. Add regression tests covering route decisions with each partial trace shape.
🤖 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.
Outside diff comments:
In `@gui/src/pages/Logs.tsx`:
- Around line 977-1015: Update the route detail rendering in the Logs component
so missing profile.revision and selected.model values use the neutral “–”
fallback (or omit the optional profile revision) instead of rendering undefined,
while preserving existing display for present values. Add regression tests
covering route decisions with each partial trace shape.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2da8f7f9-7bf6-4b0b-8cc2-8b16cd296724
📒 Files selected for processing (2)
gui/src/pages/Logs.tsxgui/tests/routing-profiles.test.tsx
[GD] Verdict: approve-commentTLDR
Full verdictSemantic propagation
Linked: none explicit UsefulnessRI-10 is a real operator control-plane surface: profile list/selection, dry-run evidence, analytics, and Logs why-this-route. Beta labeling correctly communicates not-fully-hardened ship intent. Bugs / correctness
Security
Spec / standards
Reviews
Base / CI
Simplification
Gatenone Bottom linePR |
|
@lidge-jun @Ingwannu — flagging this as the one PR on the board that is actually reviewable right now. From a triage sweep over all 25 open PRs (
The 10-commit freshness gate in Scope for whoever picks it up: 15 commits, 23 files, routing profiles plus the why-this-route views, rebased after RI-09 (#1016) merged. No blocker recorded against it — it is waiting on review capacity, not on a fix. One note on timing rather than a request: it was 4 commits behind at 13:33Z today and 9 by 13:59Z. It is not at risk yet, but the gate is 10. |
Closed #1045 on ancestry plus a green suite. Requested review on #936, #557, and #1018 - and found that #936, recorded as needing security review in four separate devlog rounds, had no reviewer assigned at all. Every round was right that it was blocked; none of them assigned anyone, so an administrative gap read as a technical one for nine days. The record also states why sixteen issues stayed open, including the four whose fix is provably on dev but whose reported symptom was never reproduced here. Closing on a shipped fix that was never shown to address the report is how an issue gets closed twice.
Add focused GUI/regression tests for the RI-10 routing page, document CLI parity for the new management endpoints, and guard dry-run responses against non-OK bodies. Include a live dashboard screenshot for the GUI PR gate.
Ship the new Routing surface labeled beta so operators can see it is available but not fully hardened yet.
Update the English PR screenshot so the nav and page title show Routing (beta).
Refresh the selected profile on reload and clear stale dry-run results when the profile or evidence inputs change.
Move dry-run clear side effects out of the setSelected updater and build dry-run evidence without NaN context tokens.
Guard overlapping reloads, reuse .tbl styles, validate cached routeDecision, localize unavailable analytics markers, and align fixtures with health scores.
Treat missing eligibility as unknown instead of healthy, and fall back to an en-dash when provider or model labels are absent.
dev now shards Windows like Linux (8034cd7). Update the ownership pin to expect 15m and a matching shard matrix instead of the superseded single-leg 40m isolate budget.
Validate string fields on cached/API routeDecision objects before render and drop malformed traces instead of crashing the Logs detail panel. Align the routing test fixture with require.imageInput.
eslint no-unused-vars rejects the _drop destructure alias; omit via copy+delete instead.
b3dae43 to
96770df
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/src/pages/RoutingProfiles.tsx`:
- Around line 106-123: In the load flow containing profilesRes.json() and
analyticsRes.json(), parse both response bodies before applying any state, then
re-check generation === loadGenerationRef.current after each body await and
immediately before updating profiles, selection, or analytics. Prevent stale
loads from overwriting newer results, and add a regression test where an older
deferred response resolves after a retry completes.
In `@tests/routing-intelligence-ui.test.ts`:
- Around line 24-30: The existing UI test only inspects source text and does not
exercise route-decision validation. Extend tests/routing-intelligence-ui.test.ts
with focused direct tests for validCachedRouteDecision and
sanitizeLogEntryRouteDecision, covering non-array or excluded candidates,
non-boolean candidate.eligible, and non-string profile.id, routeKind, and
selected.provider values while preserving valid input 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: 88732ea6-eeb9-49f3-a02c-7c31811e89b1
⛔ Files ignored due to path filters (1)
docs-site/public/pr-screenshots/1018-routing-intelligence.pngis excluded by!**/*.png
📒 Files selected for processing (22)
devlog/_plan/260804_router_intelligence/001_pr_stack_status.mddocs-site/src/content/docs/ja/reference/configuration/routing.mddocs-site/src/content/docs/ko/reference/configuration/routing.mddocs-site/src/content/docs/reference/configuration/routing.mddocs-site/src/content/docs/ru/reference/configuration/routing.mddocs-site/src/content/docs/zh-cn/reference/configuration/routing.mdgui/src/App.tsxgui/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/zh.tsgui/src/icons.tsxgui/src/pages/Logs.tsxgui/src/pages/RoutingProfiles.tsxgui/src/styles.cssgui/tests/routing-profiles.test.tsxtests/ci-workflows.test.tstests/cli-headless-parity.test.tstests/routing-intelligence-ui.test.ts
…eDecision sanitizers CodeRabbit asked for generation guards after body awaits on RoutingProfiles load, and direct tests for route-decision validation instead of source-only assertions. Extract helpers to a non-component module for react-refresh lint.
Summary
RI-10 - the final PR of the Router Intelligence / Routing Control Plane programme. Adds the dashboard surfaces (routing profiles, dry-run evaluation, routing analytics, why-this-route in the Logs detail) plus the documentation set across all locales.
Live same-origin English capture of the new Routing page with the beta labels: profile list/detail, dry-run candidate evaluation, and analytics panel.
GUI surfaces
#routing,gui/src/pages/RoutingProfiles.tsx):POST /api/routing-profiles/dry-runwith a candidate/eligibility/exclusions/score table - the selected candidate is marked;/api/routing-analytics: requests, success and fallback rates, p50/p95/p99, cooldown failures, confidence, explicit truncated-history indicator, and the provider/model breakdown.TKeyset is compile-checked.aria-pressed, keyboard-operable profile selection.Docs
reference/configuration/routing.md(all five locales) now covers:routingProfilesschema, alias/collision rules, dry-run + evaluate CLI, the combos-vs-profiles distinction, the request-history/routing-analytics/route-decision APIs, and migration notes (disposable index, no auto-tuning).Design notes
.page-head,.panel,.card,.badge,.btn,.tbl); no enterprise redesign.Privacy / security
bun run privacy:scanpasses.Compatibility
/api/*contracts unchanged.Dependency
devafter RI-09 (feat(control-plane): expose route decision explanations #1016) merged (68d3aa083).dev; this PR is the UI/docs consumer of those APIs.Non-goals
Tests / local verification
gui/tests/routing-profiles.test.tsx— mount + dry-run success/error status handling + selection/reload stale-state coveragetests/routing-intelligence-ui.test.ts— page registration + API/Logs wiring assertionstests/cli-headless-parity.test.ts— documents/api/routing-profilesand/api/routing-analyticsparity ownershipbun x tsc --noEmitbun test ./gui/tests/routing-profiles.test.tsx tests/routing-intelligence-ui.test.tsreact-doctorlocal scan clean for changed GUI files (blocking: warning)Summary by CodeRabbit
New Features
Bug Fixes
Documentation