Ordered by lowest effort and lowest risk first.
Effort: very low Risk: very low
Why:
- Defined in
internal/responsecache/semantic.go. - No call sites found in the repo.
How verified:
- Symbol searched:
CacheTypeBoth - Command:
rg -n "CacheTypeBoth"
Suggested action:
- Delete the constant and let tests confirm nothing depended on it.
Effort: low Risk: very low
Why:
- The same shape is repeated in:
internal/admin/dashboard/static/js/dashboard.jsinternal/admin/dashboard/static/js/modules/usage.jsinternal/admin/dashboard/static/js/modules/workflows.js
Suggested action:
- Keep a single
emptyCacheOverview()factory and reuse it everywhere.
Effort: low Risk: low
Why:
- The handler sets
CacheModeCachedininternal/admin/handler.go. - Each reader sets it again in:
internal/usage/reader_sqlite.gointernal/usage/reader_postgresql.gointernal/usage/reader_mongodb.go
GetCacheOverview()already implies cached-only behavior.
Suggested action:
- Keep the override in one place only.
- Prefer reader ownership so the behavior stays correct regardless of caller.
Effort: medium Risk: medium
Why:
- Production flow now uses
HandleRequest()frominternal/server/translated_inference_service.go. .Middleware()ininternal/responsecache/responsecache.gois only referenced by tests.
How verified:
- Symbols searched:
Middleware()andHandleRequest( - Commands:
rg -n "\\.Middleware\\(\\)" | sortrg -n "HandleRequest\\(" | sort
Suggested action:
- Before deleting the compatibility wrapper, keep equivalent cache-hit and cache-miss coverage around
HandleRequest(). - Existing tests in
internal/responsecache/handle_request_test.goalready cover core hit/miss flows and should be expanded first if wrapper-specific assertions are still needed. - Delete the compatibility wrapper.
- Only remove
internal/responsecache/middleware_test.goafterHandleRequest()-level coverage fully preserves the hit/miss, response header/status, and cache population assertions currently carried by the middleware wrapper tests.
Effort: medium to high Risk: medium
Why:
- Overlapping cache constants and normalization logic exist in:
internal/usage/cache_type.gointernal/auditlog/auditlog.gointernal/responsecache/semantic.go
- This increases the chance of drift when new cache types or modes are added.
Suggested action:
- Introduce a small shared internal package for cache semantics.
- Do it only if it can be done without creating import cycles.
Effort: low Risk: low
Why:
config.ResolveFallbackDefaultMode()now owns the blank-to-autodefaulting rule.internal/app/app.gostill re-implements fallback-mode parsing in:dashboardFallbackModeValue()fallbackFeatureEnabledGlobally()fallbackModeEnabled()
- Those helpers currently perform their own
TrimSpace/ case-folding instead of reusing config-owned semantics.
Suggested action:
- Add small config-owned helpers for:
- "is fallback enabled for this mode?"
- "what dashboard mode should be exposed for this config?"
- Remove the ad hoc mode parsing from
internal/app/app.go. - This keeps blank, mixed-case, and future mode handling in one place.
Effort: medium Risk: medium
Why:
internal/server/translated_inference_service.gohas two near-identical fallback loops:tryFallbackResponse()tryFallbackStream()
- Both:
- fetch selectors
- gate on
shouldAttemptFallback() - derive
providerType - log attempt/success messages
- walk candidates while preserving the last error
Suggested action:
- Extract one shared iterator that owns:
- selector traversal
- provider-type lookup
- attempt logging
- last-error handling
- Keep the typed wrappers only for the response/stream result shapes.
Effort: medium Risk: low to medium
Why:
internal/fallback/resolver.gorecomputes trimmed selector identity several times per request:sourceModelInfo()modeFor()manualSelectorsFor()matchKeys()sourceKey()
modeFor()andmanualSelectorsFor()each rebuild the same ordered match-key list.
Suggested action:
- Introduce a small internal struct for one fallback resolution pass, containing:
- source model info
- canonical source key
- ordered match keys
- Build it once in
ResolveFallbacks()and pass it through helper calls. - This would trim repeated string cleanup and make precedence rules easier to inspect.
Effort: medium Risk: low to medium
Why:
config.loadFallbackConfig()currently owns both:- fallback-mode validation/defaulting
- the custom JSON loader for
manual_rules_path
- The manual loader includes:
- duplicate raw JSON key detection
nullrejection- trailing-content validation
- whitespace normalization
- That makes the config loader harder to scan than the rest of the config pipeline.
Suggested action:
- Move the manual-rule JSON parsing into a dedicated helper or file, for example
loadFallbackManualRules(path string). - Keep
loadFallbackConfig()focused on policy validation and wiring. - Preserve the current strict error messages and test coverage while isolating the parser.
Effort: medium Risk: medium
Why:
- The managed backend default is set in
internal/app/app.go:defaultWorkflowInput(). - The dashboard draft default is separately hardcoded in
internal/admin/dashboard/static/js/modules/workflows.js:defaultWorkflowForm(). - We already changed both once to keep them aligned, which confirms the drift risk is real.
Suggested action:
- Prefer a single server-owned default surface for workflow authoring defaults.
- Options:
- expose default feature flags from the admin config endpoint
- derive the initial dashboard form from the active managed default workflow
- This reduces UI/backend drift for fallback and other workflow features.
- Remove
CacheTypeBoth. - Deduplicate the dashboard empty-state object.
- Keep cached-only policy in one layer.
- Remove the legacy middleware path.
- Centralize cache semantics in a shared package.
- Centralize fallback-mode semantics in
config. - Collapse the duplicated translated fallback attempt loops.
- Precompute fallback source identity once per resolution.
- Extract manual fallback-rule file parsing from
loadFallbackConfig. - Pick one owner for workflow fallback defaults.