feat(workflows): expose failover controls in execution plans#186
feat(workflows): expose failover controls in execution plans#186SantiagoDePolonia merged 11 commits intomainfrom
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:
📝 WalkthroughWalkthroughAdds a dashboard runtime-config endpoint and wiring (FEATURE_FALLBACK_MODE), admin endpoints to fetch execution-plan views, frontend execution-plan UI/preview and templates, persists and propagates audit log cache type (exact/semantic) and marks audit entries on cache hits, removes a token-usage design doc, and expands tests across frontend and backend. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App Init
participant Server as HTTP Server
participant Admin as Admin Handler
participant Frontend as Dashboard Frontend
participant ResponseCache as Response Cache
participant Audit as Audit subsystem
App-->>Admin: WithDashboardRuntimeConfig(map of allowlisted flags)
Server->>Admin: register GET /admin/api/v1/dashboard/config and /execution-plans/:id
Frontend->>Server: GET /admin/api/v1/dashboard/config
Server->>Admin: handle DashboardConfig request
Admin-->>Server: return allowlisted runtime config JSON
Server-->>Frontend: runtime config JSON (e.g., FEATURE_FALLBACK_MODE)
Frontend->>Frontend: update UI (failover visibility, preview behavior)
Frontend->>Server: user triggers execution/preview or requests plan version
Server->>Audit: create LogEntry and attach to request context
ResponseCache->>Audit: EnrichEntryWithCacheType("exact"/"semantic") on cache hit
Server-->>Frontend: response (may include X-Cache header)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/admin/dashboard/static/js/modules/execution-plans.js (1)
20-31:⚠️ Potential issue | 🟠 MajorDon't default hidden failover state to
truefor new workflows.When
FEATURE_FALLBACK_MODEisoff(or the config fetch fails), the toggle is hidden, but new forms still initializefeatures.fallbacktotrueandbuildExecutionPlanRequest()persists that hidden value. That means workflows created while failover is unavailable can silently opt into failover later when the global mode is turned on. Only hydrated existing plans should carry hidden fallback values forward.🩹 Suggested fix
executionPlanForm: { scope_provider: '', scope_model: '', name: '', description: '', features: { cache: true, audit: true, usage: true, guardrails: false, - fallback: true + fallback: false }, guardrails: [] }, defaultExecutionPlanForm() { + const fallbackVisible = this.executionPlanFailoverVisible(); return { scope_provider: '', scope_model: '', name: '', description: '', features: { cache: true, audit: true, usage: true, guardrails: false, - fallback: true + fallback: fallbackVisible }, guardrails: [] }; },Also applies to: 35-49, 326-339
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/admin/dashboard/static/js/modules/execution-plans.js` around lines 20 - 31, The form is initializing features.fallback to true for all new execution plans so hidden fallback toggles get persisted even when FEATURE_FALLBACK_MODE is off; change the initialization in executionPlanForm (and the other form defaults around the indicated blocks) to set features.fallback = false for new plans, and update buildExecutionPlanRequest to only carry a persisted fallback value when the form was hydrated from an existing plan (e.g., detect hydration via a flag like isHydrated or by checking if an existing plan object was loaded) so hidden fallback values are not implicitly enabled for newly created workflows.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/admin/dashboard/static/css/dashboard.css`:
- Around line 2238-2243: The 16×16 control (the rule with width: 16px; height:
16px; border-radius: 4px; border: 1px solid color-mix(...); background:
transparent) has a too-small hit area on touch — keep the 16px visual but add a
larger, invisible hit target with a pseudo-element (e.g., ::before or ::after)
that is position: absolute, centered with negative offsets or increased inset to
expand the clickable area, has transparent background, and pointer-events: auto;
ensure the original element remains visually 16×16 and retains its
border/background while the pseudo-element increases the interactive size.
In `@internal/app/app.go`:
- Around line 730-739: dashboardFallbackModeValue currently returns any
non-empty string from cfg.Fallback.DefaultMode, which can expose unsupported
modes in the UI; change it to validate the normalized mode against the allowed
values ("auto" and "manual") and return config.FallbackModeOff for any other
value. Update dashboardFallbackModeValue to normalize the mode (as it already
does), then if mode == "auto" return config.FallbackModeAuto, else if mode ==
"manual" return config.FallbackModeManual, otherwise return
config.FallbackModeOff so that it matches the behavior of
fallbackFeatureEnabledGlobally().
---
Outside diff comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 20-31: The form is initializing features.fallback to true for all
new execution plans so hidden fallback toggles get persisted even when
FEATURE_FALLBACK_MODE is off; change the initialization in executionPlanForm
(and the other form defaults around the indicated blocks) to set
features.fallback = false for new plans, and update buildExecutionPlanRequest to
only carry a persisted fallback value when the form was hydrated from an
existing plan (e.g., detect hydration via a flag like isHydrated or by checking
if an existing plan object was loaded) so hidden fallback values are not
implicitly enabled for newly created workflows.
🪄 Autofix (Beta)
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5f2915da-3d0e-4e4e-9c73-a08c503c3442
📒 Files selected for processing (14)
docs/plans/token-usage-tracking.mdinternal/admin/dashboard/static/css/dashboard.cssinternal/admin/dashboard/static/js/modules/execution-plans-layout.test.jsinternal/admin/dashboard/static/js/modules/execution-plans.jsinternal/admin/dashboard/static/js/modules/execution-plans.test.jsinternal/admin/dashboard/static/js/modules/timezone-layout.test.jsinternal/admin/dashboard/templates/index.htmlinternal/admin/handler.gointernal/admin/handler_executionplans_test.gointernal/admin/handler_test.gointernal/app/app.gointernal/app/app_test.gointernal/server/http.gointernal/server/http_test.go
💤 Files with no reviewable changes (1)
- docs/plans/token-usage-tracking.md
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/admin/dashboard/static/js/modules/audit-list.js`:
- Around line 36-38: The current flow awaits
this.prefetchAuditExecutionPlans(this.auditLog.entries) inside the outer
try/catch so any rejection clears this.auditLog; instead, split the prefetch
into its own try/catch: after successfully setting this.auditLog (and after
parse), check typeof this.prefetchAuditExecutionPlans === 'function' and then
call and await it inside a nested try/catch that catches and logs the error (or
handles it) without rethrowing, so failures in prefetchAuditExecutionPlans do
not reset or modify this.auditLog or affect the successful fetch/parse path.
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 201-239: executionPlanPreview currently calls
executionPlanSourceGuardrails which coerces blank/whitespace step strings into
0, causing previews to disagree with submit-time validation in
buildExecutionPlanRequest/validateExecutionPlanRequest; fix by reusing the same
submit-time parsing logic (or at minimum trim the step strings before Number()
conversion) inside executionPlanPreview (and the parallel hydration code paths)
so blank or whitespace-only step values remain invalid and preview/hydration
match buildExecutionPlanRequest/validateExecutionPlanRequest behavior.
- Around line 437-456: fetchExecutionPlanRuntimeConfig never uses the request
timeout/AbortController used by fetchExecutionPlansPage, so a hung
/admin/api/v1/dashboard/config fetch can stall refreshes; update
fetchExecutionPlanRuntimeConfig (and the similar block around lines 573-577) to
create an AbortController, pass its signal into fetch, start a timeout (matching
the workflow list timeout used by fetchExecutionPlansPage) that calls
controller.abort(), and clear the timeout on success/failure so the fetch will
fail fast and not block execution plan updates.
- Around line 855-883: The responseSuccess calculation in epRuntimeFromEntry
incorrectly treats only 200 as success; update responseSuccess (and consequently
aiSuccess) to consider any 2xx HTTP status as successful by checking that
statusCode is a finite number and 200 <= statusCode < 300 (ensure null/undefined
statusCode remains treated as non-success). Modify the expression that defines
responseSuccess inside epRuntimeFromEntry to use that range check so
201/204/etc. are classified as successes.
🪄 Autofix (Beta)
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f343ad0b-8600-4a3b-b93b-a26132edec42
📒 Files selected for processing (29)
internal/admin/dashboard/static/css/dashboard.cssinternal/admin/dashboard/static/js/modules/audit-list.jsinternal/admin/dashboard/static/js/modules/execution-plans-layout.test.jsinternal/admin/dashboard/static/js/modules/execution-plans.jsinternal/admin/dashboard/static/js/modules/execution-plans.test.jsinternal/admin/dashboard/static/js/modules/timezone-layout.test.jsinternal/admin/dashboard/templates/index.htmlinternal/admin/handler.gointernal/admin/handler_executionplans_test.gointernal/app/app.gointernal/app/app_test.gointernal/auditlog/auditlog.gointernal/auditlog/auditlog_test.gointernal/auditlog/middleware.gointernal/auditlog/reader_mongodb.gointernal/auditlog/reader_mongodb_test.gointernal/auditlog/reader_postgresql.gointernal/auditlog/reader_sqlite.gointernal/auditlog/store_postgresql.gointernal/auditlog/store_postgresql_test.gointernal/auditlog/store_sqlite.gointernal/auditlog/store_sqlite_test.gointernal/auditlog/stream_wrapper.gointernal/executionplans/service.gointernal/responsecache/handle_request_test.gointernal/responsecache/semantic.gointernal/responsecache/semantic_test.gointernal/responsecache/simple.gointernal/server/http.go
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
internal/admin/dashboard/static/js/modules/execution-plans.js (2)
267-279:⚠️ Potential issue | 🟡 MinorKeep preview and hydration aligned with integer-only guardrail steps.
executionPlanSourceGuardrails()now fixes blank values, but it still passes-1and1.5because the filter only checksNumber.isFinite.executionPlanPreview()andopenExecutionPlanCreate()both reuse this helper, so the UI can still render guardrail steps thatvalidateExecutionPlanRequest()rejects on submit.Suggested fix
return raw .map((step) => ({ ref: String(step && step.ref || '').trim(), step: this.parseExecutionPlanGuardrailStep(step && step.step) })) - .filter((step) => Number.isFinite(step.step)); + .filter((step) => Number.isInteger(step.step) && step.step >= 0);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/admin/dashboard/static/js/modules/execution-plans.js` around lines 267 - 279, The helper executionPlanSourceGuardrails currently filters guardrails with Number.isFinite, which still allows non-integer and negative values (e.g., -1, 1.5); change the final filter to require integer-only, positive steps (e.g., replace .filter((step) => Number.isFinite(step.step)) with .filter((step) => Number.isInteger(step.step) && step.step > 0)) so executionPlanPreview and openExecutionPlanCreate (which reuse this helper) will only render guardrails that match validateExecutionPlanRequest’s integer-only constraints.
402-439:⚠️ Potential issue | 🟠 MajorDon't preserve a hidden fallback bit after the user retargets the workflow scope.
includeFallbackis currently keyed offexecutionPlanFormHydrated, so editing an existing workflow and then changingscope_provider/scope_modelwhileFEATURE_FALLBACK_MODEis off will still POST the old hiddenfeatures.fallbackvalue. Because the toggle and preview hint are both hidden in that mode, operators can unintentionally stamp inherited failover state onto a brand-new scope.Suggested fix
const features = form.features || {}; - const includeFallback = this.executionPlanFailoverVisible() || !!this.executionPlanFormHydrated; + const preserveHydratedFallback = !!this.executionPlanFormHydrated && this.executionPlanScopeMatches( + { scope: this.executionPlanHydratedScope }, + { scope_provider: provider, scope_model: model } + ); + const includeFallback = this.executionPlanFailoverVisible() || preserveHydratedFallback;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/admin/dashboard/static/js/modules/execution-plans.js` around lines 402 - 439, The current includeFallback logic preserves a hidden fallback flag when an existing plan is edited and the scope is retargeted; update buildExecutionPlanRequest so includeFallback is true only if the failover controls are visible (executionPlanFailoverVisible()) OR the current form explicitly contains a user-set features.fallback and the user has not retargeted the scope (compare the current provider/model to the original this.executionPlanForm.scope_provider/scope_model to detect retargeting); reference buildExecutionPlanRequest, executionPlanFormHydrated, executionPlanFailoverVisible, and features.fallback when making this change so hidden fallback bits are not carried to a new scope.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 814-843: executionPlanAuditChart can be called with source ===
null which causes executionPlanChartModel to suppress audit/async nodes; modify
executionPlanChartModel (or callsite) so the audit path is forced for
audit-chart rendering: update executionPlanChartModel(source, runtime, options)
to respect an explicit options.forceAudit (and options.forceAsync if needed) or
have executionPlanAuditChart call executionPlanChartModel with { forceCache:
true, forceAudit: true, forceAsync: true }; ensure the computed properties
showAudit and showAsync use options.forceAudit/options.forceAsync or fall back
to epHasAudit(source)/epHasAsync(source) so the Audit Log node is always shown
for executionPlanAuditChart.
In `@internal/admin/dashboard/templates/audit-pane.html`:
- Line 5: The "Copy Body" button lacks an explicit button type which can cause
it to act as a submit inside forms; update the button element that calls
copyAuditJSON (the button with class "pagination-btn" and
`@click.prevent`="copyAuditJSON({{.}}.copyBody, $event)") to include type="button"
so it does not trigger form submission.
In `@internal/admin/dashboard/templates/pagination.html`:
- Around line 5-6: The pagination buttons currently lack an explicit type and
will default to type="submit"; update the two button elements (the ones with
class "pagination-btn" and click handlers `@click`="{{.}}PrevPage()" and
`@click`="{{.}}NextPage()") to include type="button" so they never trigger form
submission when rendered inside a <form>, keeping existing :disabled bindings
({{.}}.offset, {{.}}.limit, {{.}}.total) and click handler names unchanged.
---
Outside diff comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 267-279: The helper executionPlanSourceGuardrails currently
filters guardrails with Number.isFinite, which still allows non-integer and
negative values (e.g., -1, 1.5); change the final filter to require
integer-only, positive steps (e.g., replace .filter((step) =>
Number.isFinite(step.step)) with .filter((step) => Number.isInteger(step.step)
&& step.step > 0)) so executionPlanPreview and openExecutionPlanCreate (which
reuse this helper) will only render guardrails that match
validateExecutionPlanRequest’s integer-only constraints.
- Around line 402-439: The current includeFallback logic preserves a hidden
fallback flag when an existing plan is edited and the scope is retargeted;
update buildExecutionPlanRequest so includeFallback is true only if the failover
controls are visible (executionPlanFailoverVisible()) OR the current form
explicitly contains a user-set features.fallback and the user has not retargeted
the scope (compare the current provider/model to the original
this.executionPlanForm.scope_provider/scope_model to detect retargeting);
reference buildExecutionPlanRequest, executionPlanFormHydrated,
executionPlanFailoverVisible, and features.fallback when making this change so
hidden fallback bits are not carried to a new scope.
🪄 Autofix (Beta)
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4514d7e6-6345-47ef-83e7-986b5b3d1366
📒 Files selected for processing (15)
internal/admin/dashboard/dashboard.gointernal/admin/dashboard/static/css/dashboard.cssinternal/admin/dashboard/static/js/modules/audit-list.jsinternal/admin/dashboard/static/js/modules/audit-list.test.jsinternal/admin/dashboard/static/js/modules/dashboard-layout.test.jsinternal/admin/dashboard/static/js/modules/execution-plans-layout.test.jsinternal/admin/dashboard/static/js/modules/execution-plans.jsinternal/admin/dashboard/static/js/modules/execution-plans.test.jsinternal/admin/dashboard/static/js/modules/timezone-layout.test.jsinternal/admin/dashboard/templates/audit-pane.htmlinternal/admin/dashboard/templates/auth-banner.htmlinternal/admin/dashboard/templates/execution-plan-chart.htmlinternal/admin/dashboard/templates/helper-disclosure.htmlinternal/admin/dashboard/templates/index.htmlinternal/admin/dashboard/templates/pagination.html
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)
internal/admin/dashboard/static/js/modules/execution-plans.js (1)
407-445:⚠️ Potential issue | 🟠 MajorPreserve hidden fallback from the matched active workflow, not only hydrated forms.
executionPlanSubmitMode()already treats a fresh form as a save when the current scope matches an active workflow, butbuildExecutionPlanRequest()only carriesplan_payload.features.fallbackacross whenexecutionPlanFormHydratedis true. WithFEATURE_FALLBACK_MODEhidden, saving over an existing scope from a non-hydrated form dropsfallback; on reloadexecutionPlanSourceFeatures()interprets that omission astrue, so an explicitly disabled fallback is silently re-enabled.Suggested fix
const features = form.features || {}; - const hydratedScope = this.executionPlanHydratedScope || { - scope_provider: '', - scope_model: '' - }; - const sameHydratedScope = String(hydratedScope.scope_provider || '').trim() === provider - && String(hydratedScope.scope_model || '').trim() === model; + const currentScope = { + scope_provider: provider, + scope_model: model + }; + const activeScopePlan = (Array.isArray(this.executionPlans) ? this.executionPlans : []) + .find((plan) => this.executionPlanScopeMatches(plan, currentScope)) || null; const includeFallback = this.executionPlanFailoverVisible() - || (!!this.executionPlanFormHydrated - && sameHydratedScope - && Object.prototype.hasOwnProperty.call(features, 'fallback')); + || !!activeScopePlan; @@ if (includeFallback) { - payload.plan_payload.features.fallback = !!features.fallback; + payload.plan_payload.features.fallback = this.executionPlanFailoverVisible() + ? !!features.fallback + : this.executionPlanSourceFeatures(activeScopePlan).fallback; }Please add a regression test for the non-hydrated save path too.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/admin/dashboard/static/js/modules/execution-plans.js` around lines 407 - 445, buildExecutionPlanRequest currently only preserves plan_payload.features.fallback when this.executionPlanFormHydrated is true, which drops an explicitly disabled fallback when saving a fresh (non-hydrated) form that matches the active workflow; update the includeFallback logic to also preserve the existing fallback when the current scope matches executionPlanHydratedScope (use the sameHydratedScope boolean and check Object.prototype.hasOwnProperty.call(features, 'fallback')) even if executionPlanFormHydrated is false, so payload.plan_payload.features.fallback is set whenever the active workflow has a defined fallback; add a regression test covering the non-hydrated save path (exercise executionPlanSubmitMode + buildExecutionPlanRequest for a fresh form whose scope equals executionPlanHydratedScope to assert fallback is preserved).
♻️ Duplicate comments (1)
internal/admin/dashboard/static/js/modules/execution-plans.js (1)
664-719:⚠️ Potential issue | 🟠 MajorThis repeats the earlier hung-fetch pattern on historical version lookups.
executionPlanVersionRequests[normalizedID]holds the in-flight promise until it settles. Without an abort timeout here, one stalled/admin/api/v1/execution-plans/:idrequest leaves that cache slot pinned forever, so every later prefetch for the same version ID reuses the same never-resolving promise and audit charts never recover.Suggested fix
const request = (async () => { + const controller = typeof AbortController === 'function' ? new AbortController() : null; + const timeoutID = controller && typeof setTimeout === 'function' + ? setTimeout(() => controller.abort(), 10000) + : null; try { - const res = await fetch('/admin/api/v1/execution-plans/' + encodeURIComponent(normalizedID), { - headers: this.headers() - }); + const options = { headers: this.headers() }; + if (controller) { + options.signal = controller.signal; + } + const res = await fetch('/admin/api/v1/execution-plans/' + encodeURIComponent(normalizedID), options); @@ } catch (e) { console.error('Failed to fetch workflow version:', e); return null; } finally { + if (timeoutID !== null && typeof clearTimeout === 'function') { + clearTimeout(timeoutID); + } if (this.executionPlanVersionRequests) { delete this.executionPlanVersionRequests[normalizedID]; } } })();Please mirror the existing abort/clear-timeout coverage here as well.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/admin/dashboard/static/js/modules/execution-plans.js` around lines 664 - 719, The fetchExecutionPlanVersion function leaves executionPlanVersionRequests[normalizedID] pinned if a fetch stalls; mirror the abort/clear-timeout pattern used elsewhere by creating an AbortController, pass its signal into fetch('/admin/api/v1/execution-plans/' + encodeURIComponent(normalizedID), { headers: this.headers(), signal }), start a timeout that calls controller.abort() and deletes executionPlanVersionRequests[normalizedID], clear that timeout when the request completes successfully or errors, and ensure the existing finally block still deletes executionPlanVersionRequests[normalizedID]; keep using cacheExecutionPlanVersion, cacheMissingExecutionPlanVersion and handleFetchResponse exactly as currently used for success/404/401/error branches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 407-445: buildExecutionPlanRequest currently only preserves
plan_payload.features.fallback when this.executionPlanFormHydrated is true,
which drops an explicitly disabled fallback when saving a fresh (non-hydrated)
form that matches the active workflow; update the includeFallback logic to also
preserve the existing fallback when the current scope matches
executionPlanHydratedScope (use the sameHydratedScope boolean and check
Object.prototype.hasOwnProperty.call(features, 'fallback')) even if
executionPlanFormHydrated is false, so payload.plan_payload.features.fallback is
set whenever the active workflow has a defined fallback; add a regression test
covering the non-hydrated save path (exercise executionPlanSubmitMode +
buildExecutionPlanRequest for a fresh form whose scope equals
executionPlanHydratedScope to assert fallback is preserved).
---
Duplicate comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 664-719: The fetchExecutionPlanVersion function leaves
executionPlanVersionRequests[normalizedID] pinned if a fetch stalls; mirror the
abort/clear-timeout pattern used elsewhere by creating an AbortController, pass
its signal into fetch('/admin/api/v1/execution-plans/' +
encodeURIComponent(normalizedID), { headers: this.headers(), signal }), start a
timeout that calls controller.abort() and deletes
executionPlanVersionRequests[normalizedID], clear that timeout when the request
completes successfully or errors, and ensure the existing finally block still
deletes executionPlanVersionRequests[normalizedID]; keep using
cacheExecutionPlanVersion, cacheMissingExecutionPlanVersion and
handleFetchResponse exactly as currently used for success/404/401/error
branches.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: da6970df-b94c-4f12-adac-7c42b1bac6d7
📒 Files selected for processing (5)
internal/admin/dashboard/static/js/modules/dashboard-layout.test.jsinternal/admin/dashboard/static/js/modules/execution-plans.jsinternal/admin/dashboard/static/js/modules/execution-plans.test.jsinternal/admin/dashboard/templates/audit-pane.htmlinternal/admin/dashboard/templates/pagination.html
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 764-807: fetchExecutionPlanVersion is missing an
AbortController/timeout and can hang; modify the async request block in
fetchExecutionPlanVersion to create an AbortController, pass its signal into
fetch, and set a 10_000 ms setTimeout that calls controller.abort(); clear the
timeout when fetch completes (in try/catch/finally) to avoid leaks; ensure the
abort is handled similarly to other fetches (call handleFetchResponse for
non-ok/401/404 as before or treat abort as null) and keep the existing cleanup
that deletes this.executionPlanVersionRequests[normalizedID].
In `@internal/admin/handler.go`:
- Around line 27-33: The handler currently exposes runtimeConfig as
map[string]string on the handler struct which makes the /dashboard/config
response shape open-ended; replace that with a named, strongly-typed response
struct (e.g., DashboardConfigResponse with the six explicit fields) and update
the handler method that serves /dashboard/config to populate and return that
struct, keeping any internal allowlist/filtering logic inside the handler (do
not expose the map). Update other usages of runtimeConfig in the file to
construct or read the typed struct as needed and apply the same change where
runtimeConfig is used in the other referenced blocks (the other handler methods
around the same file) so serialization, docs, and type safety are preserved.
In `@internal/app/app_test.go`:
- Around line 79-85: The test builds a config where ResponseCacheConfig.Simple
is assigned a value but that field is declared as a pointer
(*config.SimpleCacheConfig); change the composite literal to provide a pointer
by using &config.SimpleCacheConfig{...} for the Simple field (inside the Cache:
config.CacheConfig block), keeping the nested Redis:
&config.RedisResponseConfig{ URL: "redis://localhost:6379" } as-is so the types
match and the test compiles.
In `@internal/app/app.go`:
- Around line 724-727: dashboardFallbackModeValue currently reads only
cfg.Fallback.DefaultMode so the dashboard can show "off" even when per-model
overrides enable fallback; update
dashboardRuntimeConfig/dashboardFallbackModeValue to compute visibility using
the same logic as fallbackFeatureEnabledGlobally (i.e., treat any per-model
override of manual/auto as enabling fallback) rather than only DefaultMode, and
apply the same change to the other uses of dashboardFallbackModeValue in the
surrounding block (the code noted around dashboardRuntimeConfig and the related
742-755 region) so the dashboard visibility matches the compiled execution plan
behavior.
- Around line 789-790: The function simpleResponseCacheConfiguredFromResponse
dereferences cfg.Simple without guarding against nil; update it to first check
cfg.Simple != nil (and still check cfg.Simple.Redis != nil) before accessing
Redis.URL so deployments that omit the simple-cache block won't panic—i.e.,
change the conditional in simpleResponseCacheConfiguredFromResponse to ensure
cfg.Simple and cfg.Simple.Redis are non-nil and that Redis.URL is non-empty (use
strings.TrimSpace) so responseCacheConfigured() can safely call it.
🪄 Autofix (Beta)
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: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ef23a81e-d04a-4597-9e16-4f8e9afb1d4f
📒 Files selected for processing (8)
internal/admin/dashboard/static/js/modules/execution-plans-layout.test.jsinternal/admin/dashboard/static/js/modules/execution-plans.jsinternal/admin/dashboard/static/js/modules/execution-plans.test.jsinternal/admin/dashboard/templates/index.htmlinternal/admin/handler.gointernal/admin/handler_test.gointernal/app/app.gointernal/app/app_test.go
| Cache: config.CacheConfig{ | ||
| Response: config.ResponseCacheConfig{ | ||
| Simple: config.SimpleCacheConfig{ | ||
| Redis: &config.RedisResponseConfig{ | ||
| URL: "redis://localhost:6379", | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Use a pointer for ResponseCacheConfig.Simple in this test.
config.ResponseCacheConfig.Simple is defined as *config.SimpleCacheConfig, so the composite literal at Lines 81-85 will not compile as a value.
🛠️ Proposed fix
- Simple: config.SimpleCacheConfig{
+ Simple: &config.SimpleCacheConfig{
Redis: &config.RedisResponseConfig{
URL: "redis://localhost:6379",
},
},📝 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.
| Cache: config.CacheConfig{ | |
| Response: config.ResponseCacheConfig{ | |
| Simple: config.SimpleCacheConfig{ | |
| Redis: &config.RedisResponseConfig{ | |
| URL: "redis://localhost:6379", | |
| }, | |
| }, | |
| Cache: config.CacheConfig{ | |
| Response: config.ResponseCacheConfig{ | |
| Simple: &config.SimpleCacheConfig{ | |
| Redis: &config.RedisResponseConfig{ | |
| URL: "redis://localhost:6379", | |
| }, | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/app/app_test.go` around lines 79 - 85, The test builds a config
where ResponseCacheConfig.Simple is assigned a value but that field is declared
as a pointer (*config.SimpleCacheConfig); change the composite literal to
provide a pointer by using &config.SimpleCacheConfig{...} for the Simple field
(inside the Cache: config.CacheConfig block), keeping the nested Redis:
&config.RedisResponseConfig{ URL: "redis://localhost:6379" } as-is so the types
match and the test compiles.
Summary
FEATURE_FALLBACK_MODE?control and simplified helper copy stylingTest Plan
Summary by CodeRabbit
New Features
Behavior
Style
Documentation