Skip to content

refactor(failover): unify feature naming on "failover"#451

Merged
SantiagoDePolonia merged 2 commits into
mainfrom
chore/fallback-unify
Jul 1, 2026
Merged

refactor(failover): unify feature naming on "failover"#451
SantiagoDePolonia merged 2 commits into
mainfrom
chore/fallback-unify

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What & why

The model-failover feature was referred to as both failover and fallback, and lived in two packages (internal/failover for the store/service, internal/fallback for the resolver). This unifies the feature on failover — the precise term for switching to a backup model when the primary fails, and the one already used by the DB tables (failover_rules), the admin handler, the audit-log snapshot, and the gateway attempt kind.

fallback stays reserved for its unrelated generic uses (provider-type defaults, CONFIGURED_PROVIDER_MODELS_MODE=fallback, pricing/registry fallbacks, generic default-value params).

Changes

  • Merged the two directories → one internal/failover/ package (resolver moved in; internal/fallback/ removed).
  • Renamed feature identifiers Fallback*Failover* across config, gateway (fallback.gofailover.go), server, app, admin, core, workflows, auditlog, live — types, functions, the FailoverResolver interface, the workflow FailoverEnabled() toggle, and the WithFailoverUsed/GetFailoverUsed context flag.
  • Config surface unified: FailoverConfig, the failover: YAML block, FAILOVER_MANUAL_RULES_PATH + FAILOVER_MODE env vars, config/failover.go, config/failover.example.json, .env.template.
  • Workflow feature flag features.fallbackfeatures.failover (Go, dashboard JS/HTML, generated docs, e2e scenario) — the flag set is now internally consistent: cache/audit/usage/budget/guardrails/failover.
  • Regenerated swagger/openapi (this also surfaces the feat: add manual failover management #444 failover admin API, which the committed generated docs were missing) and updated feature .mdx prose.

Deliberately kept as fallback

  • The rule data fields primary_model / fallback_models (DB columns, JSON API, dashboard labels). They describe the target role ("the models you fall back to"), pair naturally with primary_model, and renaming would force a needless SQLite/Postgres/Mongo migration + API break while the feature identity (routes /admin/failover, table failover_rules, package, types) is already failover.

User-visible / breaking

The failover feature is fresh (#444), so its env vars, config key, and the serialized features.failover workflow flag are renamed outright rather than aliased:

Before After
fallback: (config.yaml block) failover:
FALLBACK_MANUAL_RULES_PATH FAILOVER_MANUAL_RULES_PATH
FEATURE_FALLBACK_MODE (deprecated) FAILOVER_MODE
workflow features.fallback features.failover
config/fallback.example.json config/failover.example.json

The admin failover API (/admin/failover, primary_model/fallback_models) is unchanged.

Testing

  • gofmt clean · go build -tags=swagger ./... · full go test ./... · tagged tests compile
  • Dashboard JS: 384/384 pass
  • Pre-commit hooks (test-race, lint, dashboard tests, mint validate) pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added admin failover management actions, including viewing, updating (upsert), deleting, generating, and resetting failover rules.
    • Added live usage throughput reporting and enhanced audit logging with per-provider attempt details.
    • Updated workflow execution and dashboards to use failover settings end-to-end (forms, previews, effective feature handling, and execution metadata).
  • Documentation

    • Renamed “fallback” to “failover” across configuration examples, environment variables, UI labels, and API/OpenAPI schemas.
    • Updated failover-related config blocks, sample rule files, and feature payloads to match the new terminology.

The model-failover feature was referred to as both "failover" and
"fallback", with two packages (internal/failover for the store/service
and internal/fallback for the resolver). Unify on "failover" — the
precise term for switching to a backup model when the primary fails,
and the one already used by the DB tables (failover_rules), admin
handler, audit-log snapshot, and gateway attempt kind. "fallback" stays
reserved for its unrelated generic uses (provider-type defaults,
CONFIGURED_PROVIDER_MODELS_MODE=fallback, pricing/registry fallbacks).

- Merge internal/fallback (resolver) into internal/failover — one package.
- Rename feature identifiers Fallback*->Failover* across config, gateway
  (fallback.go->failover.go), server, app, admin, core, workflows,
  auditlog, and live (types, funcs, the FailoverResolver interface, the
  workflow FailoverEnabled() toggle, the WithFailoverUsed context flag).
- Config surface: FailoverConfig, the `failover:` YAML block,
  FAILOVER_MANUAL_RULES_PATH + FAILOVER_MODE env vars,
  config/failover.go, config/failover.example.json, .env.template.
- Workflow feature flag features.fallback -> features.failover (Go,
  dashboard JS/HTML, generated docs, e2e scenario).
- Regenerate swagger/openapi; update feature docs.

Kept as "fallback" deliberately: the rule data fields primary_model /
fallback_models (DB columns + JSON API + dashboard labels) describe the
target role and pair naturally with primary_model; renaming would force
a needless SQLite/Postgres/Mongo migration and API break while the
feature identity (routes, table, package, types) is already failover.

The feature is fresh (#444), so env vars / config key / the serialized
features.failover flag are renamed outright rather than aliased.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7c6e3a4f-3d0b-430f-995e-99ed164da00c

📥 Commits

Reviewing files that changed from the base of the PR and between 59dc99d and 1270156.

📒 Files selected for processing (4)
  • internal/admin/handler_workflows_test.go
  • internal/gateway/failover.go
  • internal/server/http.go
  • tests/e2e/release-e2e-scenarios.md

📝 Walkthrough

Walkthrough

This PR renames the “fallback” concept to “failover” across configuration, workflow feature flags, routing/execution, server wiring, admin workflow management, audit logging, generated API docs, and supporting tests and documentation.

Changes

Fallback to Failover Rename

Layer / File(s) Summary
Config schema and loading for failover
config/config.go, config/failover.go, config/config.example.yaml, .env.template, config/config_test.go
Config.Fallback becomes Config.Failover; failover config loading now parses failover mode, manual rules, disabled models, and updated example/env/test coverage.
Core workflow feature flags
internal/core/context.go, internal/core/workflow.go, internal/workflows/*, tests/integration/workflows_guardrails_test.go, tools/seed-demo-data.sh, internal/auditlog/*, internal/live/broker_test.go
Request-context helpers and workflow feature fields move from Fallback/fallback-used to Failover/failover-used, with matching audit and workflow test updates.
Failover resolver and attempt routing
internal/failover/*, internal/gateway/*
Resolver APIs, orchestrator wiring, and translated request retry helpers switch from fallback to failover naming and request markers across response, stream, and embedding paths.
Server request dispatch and failover markers
internal/server/*, internal/responsecache/*
HTTP/server handler wiring uses failover resolver dependencies, and chat/responses execution marks UsedFailover for cache/audit behavior.
Application wiring and workflow feature flags
internal/app/app.go, internal/app/app_test.go, internal/admin/handler_workflows_test.go
App startup, dashboard runtime config, admin workflow handler tests, and workflow feature caps are updated to pass and assert failover flags and resolver wiring.
Admin failover handler and workflow HTTP tests
internal/admin/handler_failover.go, internal/admin/handler_failover_test.go, internal/admin/handler_workflows_test.go
Failover rule generation uses the new resolver API, and workflow CRUD/list/get tests switch stored feature fields and expectations to failover.
Admin dashboard workflow UI failover toggle
internal/admin/dashboard/static/js/modules/workflows.js, internal/admin/dashboard/static/js/modules/workflows.test.cjs, internal/admin/dashboard/templates/page-workflows.html
The dashboard’s workflow editor, preview, charts, and request builder now use failover payload fields, labels, and visibility logic, with matching tests and template bindings.
Generated API docs and documentation updates
cmd/gomodel/docs/docs.go, docs/openapi.json, docs/*.mdx, docs/dev/possible-refactoring.md, tests/e2e/*, tests/integration/*
Generated docs, OpenAPI, user docs, e2e scenarios, and integration fixtures are updated to expose failover endpoints/schemas and use failover terminology in examples.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • ENTERPILOT/GoModel#236: It introduced the failover documentation page that this PR updates to the new terminology and config keys.
  • ENTERPILOT/GoModel#239: It touches the same workflow/audit-log failover plumbing that this PR renames from fallback to failover.
  • ENTERPILOT/GoModel#444: It added the manual failover management path that this PR rewires to the new failover resolver/config APIs.

Poem

I hopped from fallback into failover light,
With configs and handlers all tucked in right.
The docs got a nibble, the tests got a cheer,
And this rabbit says: “rename season is here.” 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main change: renaming feature terminology and wiring from fallback to failover.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/fallback-unify

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mintlify

mintlify Bot commented Jul 1, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jul 1, 2026, 12:37 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@codecov-commenter

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown

Confidence Score: 3/5

The rename is mechanically thorough and logic is unchanged, but stored workflows that had failover explicitly disabled will silently have it re-enabled after the JSON key rename, with no migration or backward-compat shim.

The overwhelming majority of changes are safe identifier renames with no behavioral change. The one real data concern is the FeatureFlags JSON/BSON key rename: any workflow persisted with "fallback": false will deserialize with Failover == nil and then be canonicalized to true, enabling failover for users who deliberately opted out. The PR treats this as acceptable given the feature's freshness, but no migration or read-compat path is provided, so any early adopter who stored an explicit opt-out could be silently affected on upgrade.

internal/workflows/types.go — the JSON/BSON tag change on FeatureFlags.Failover is the only place where the rename has a non-obvious runtime data effect.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B[translatedInferenceService]
    B --> C[InferenceOrchestrator]
    C --> D{Primary Model Attempt}
    D -->|success| E[Return Response]
    D -->|error| F{ShouldAttemptFailover?}
    F -->|no| G[Return Error]
    F -->|yes| H[FailoverSelectors via FailoverResolver]
    H --> I{workflow.FailoverEnabled?}
    I -->|no| G
    I -->|yes| J[failover.Resolver.ResolveFailovers]
    J --> K{Manual rules / DB rules}
    K --> L[Loop over failover candidates]
    L --> M{modelAuthorizer.AllowsModel?}
    M -->|no, skip| L
    M -->|yes| N[Call failover model]
    N -->|success| O[Mark WithFailoverUsed ctx - Set Meta.UsedFailover=true - Skip response cache write]
    N -->|fail| L
    L -->|exhausted| G
    O --> E
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Incoming Request] --> B[translatedInferenceService]
    B --> C[InferenceOrchestrator]
    C --> D{Primary Model Attempt}
    D -->|success| E[Return Response]
    D -->|error| F{ShouldAttemptFailover?}
    F -->|no| G[Return Error]
    F -->|yes| H[FailoverSelectors via FailoverResolver]
    H --> I{workflow.FailoverEnabled?}
    I -->|no| G
    I -->|yes| J[failover.Resolver.ResolveFailovers]
    J --> K{Manual rules / DB rules}
    K --> L[Loop over failover candidates]
    L --> M{modelAuthorizer.AllowsModel?}
    M -->|no, skip| L
    M -->|yes| N[Call failover model]
    N -->|success| O[Mark WithFailoverUsed ctx - Set Meta.UsedFailover=true - Skip response cache write]
    N -->|fail| L
    L -->|exhausted| G
    O --> E
Loading

Comments Outside Diff (1)

  1. internal/gateway/failover.go, line 98-104 (link)

    P2 The fallback parameter name here refers specifically to the failover callback, not a generic default-value fallback. The PR deliberately keeps fallback for unrelated generic uses, but this is a named failover path inside executeWithFailoverResponse. Renaming to failoverFn or attempt would make the intent consistent with the rest of the renamed surface.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (1): Last reviewed commit: "refactor(failover): unify feature naming..." | Re-trigger Greptile

Comment on lines +72 to 83
Failover *bool `json:"failover,omitempty" bson:"failover,omitempty"`
}

func (f FeatureFlags) canonicalize() FeatureFlags {
if f.Budget == nil {
budgetEnabled := true
f.Budget = &budgetEnabled
}
if f.Fallback == nil {
fallbackEnabled := true
f.Fallback = &fallbackEnabled
if f.Failover == nil {
failoverEnabled := true
f.Failover = &failoverEnabled
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Silent failover re-enablement for existing persisted workflows

Any workflow stored in DB/BSON with "fallback": false will deserialize into the new struct with Failover == nil (the old key is simply ignored). canonicalize() then defaults nil to &true, silently re-enabling failover for a workflow that had it explicitly disabled. The PR acknowledges this as a deliberate breaking change for a "fresh" feature, but the breakage is asymmetric: stored false silently flips to true, while stored true is equally silent but produces the same result — so the only affected users are those who opted out. A backward-compatible read of the old key (e.g. a custom UnmarshalJSON that also checks "fallback") or a one-off DB migration would prevent unintended silent re-enablement on upgrade.

Comment thread internal/server/http.go Outdated
ModelAuthorizer RequestModelAuthorizer // Optional: request-scoped concrete model access controller
WorkflowPolicyResolver RequestWorkflowPolicyResolver // Optional: persisted workflow resolver used during workflow resolution
FallbackResolver RequestFallbackResolver // Optional: translated-route fallback resolver
FailoverResolver RequestFailoverResolver // Optional: translated-route fallback resolver

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The field comment was not updated during the rename — it still says "fallback resolver" instead of "failover resolver".

Suggested change
FailoverResolver RequestFailoverResolver // Optional: translated-route fallback resolver
FailoverResolver RequestFailoverResolver // Optional: translated-route failover resolver

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/admin/handler_failover.go (1)

191-216: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Pass the loaded failover config into the resolver.

config.FailoverConfig{Enabled: true} strips the configured disabled-model state before SuggestFailovers runs. internal/failover/resolver.go only suppresses suggestions through cfg.Disabled, so /admin/failover/generate can still propose rules for models that failover.disabled_models has explicitly turned off. Please build this resolver from the same failover config the runtime uses instead of a synthetic enabled-only config.

🤖 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 `@internal/admin/handler_failover.go` around lines 191 - 216, The failover
resolver is being built with a synthetic always-enabled config, which drops the
loaded disabled-model state before SuggestFailovers runs. Update the resolver
construction in the admin failover handler to use the same loaded failover
config that the runtime uses, so Resolver.SuggestFailovers can respect
cfg.Disabled and avoid proposing rules for models marked in
failover.disabled_models. Keep the change localized around the resolver setup in
the handler and the failover.NewResolverWithRuleProvider call.
internal/admin/handler_workflows_test.go (1)

424-452: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Assert the positive failover value, not just the key presence.

Line 424 proves the response includes a failover key, but this test never proves the renamed field survives as true in either effective_features or body.Payload.Features. A regression that always serialized failover: false would still pass here. As per coding guidelines, **/*_test.go: Add or update tests for behavior changes, and ensure tests cover request translation, response normalization, error handling, default configuration, and provider-specific parameter mapping.

🤖 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 `@internal/admin/handler_workflows_test.go` around lines 424 - 452, The
workflow response test only checks that the failover field exists, not that it
remains enabled; update the assertions around effectiveFeatures and
workflows.View in handler_workflows_test to verify failover is true in both the
normalized response map and body.Payload.Features. Use the existing symbols
effectiveFeatures, body.Payload.Features, and workflows.View to locate the
checks and add a positive assertion for failover alongside the current
usage/audit/guardrails validations.

Source: Coding guidelines

🤖 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 `@cmd/gomodel/docs/docs.go`:
- Around line 4613-4620: The request schemas for admin.deleteFailoverRuleRequest
(and the matching object at the other referenced location) currently expose
primary_model as optional even though the handler requires it. Update the schema
definitions in docs.go to mark primary_model as required with required:
["primary_model"], then regenerate the OpenAPI output so docs/openapi.json stays
in sync.

In `@internal/admin/dashboard/templates/page-workflows.html`:
- Line 130: The checkbox bound to workflowForm.features.failover is still
unlabeled, so update the workflow form markup to associate it with a visible
label by either wrapping the input in a label element or giving the input a
unique id and matching it with a for attribute; keep the change localized to the
failover control in the page-workflows template.

In `@internal/auditlog/auditlog.go`:
- Line 159: The `WorkflowFeaturesSnapshot` field rename from `fallback` to
`failover` breaks reads of existing audit records because older documents still
store `workflow_features.fallback`. Add a backward-compatible deserialization
path in `auditlog.go` so `Failover` is populated from either legacy `fallback`
or the new key, or perform a migration/backfill before changing the BSON/JSON
tags. Keep the fix localized around `WorkflowFeaturesSnapshot` and its audit log
read/write handling so historical entries still surface the flag correctly.

In `@internal/gateway/refactor_findings_test.go`:
- Around line 217-220: The test setup for stream failover is seeding only
"fallback", so the primary path in streamFailoverProvider is still resolving to
nil instead of an empty stream. Update
TestStreamResponsesFallsBackAfterEmptyPrimaryStream to explicitly seed "primary"
in streamsByModel with an empty io.ReadCloser so the test exercises the
empty-stream fallback behavior in streamFailoverProvider.streamForModel rather
than the nil-stream path.

In `@tests/e2e/release-e2e-scenarios.md`:
- Line 1348: Update the release e2e read-back check for the workflow payload to
assert the renamed failover feature alongside cache. In the scenario that posts
`workflow_payload.features` for the managed-key release flow, extend the
existing validation so it explicitly verifies both
`workflow_payload.features.failover` and `effective_features.failover` using the
same read-back step identifiers used for the current cache assertion. This will
ensure the scenario fails if the server drops, renames, or misnormalizes the new
failover key.

---

Outside diff comments:
In `@internal/admin/handler_failover.go`:
- Around line 191-216: The failover resolver is being built with a synthetic
always-enabled config, which drops the loaded disabled-model state before
SuggestFailovers runs. Update the resolver construction in the admin failover
handler to use the same loaded failover config that the runtime uses, so
Resolver.SuggestFailovers can respect cfg.Disabled and avoid proposing rules for
models marked in failover.disabled_models. Keep the change localized around the
resolver setup in the handler and the failover.NewResolverWithRuleProvider call.

In `@internal/admin/handler_workflows_test.go`:
- Around line 424-452: The workflow response test only checks that the failover
field exists, not that it remains enabled; update the assertions around
effectiveFeatures and workflows.View in handler_workflows_test to verify
failover is true in both the normalized response map and body.Payload.Features.
Use the existing symbols effectiveFeatures, body.Payload.Features, and
workflows.View to locate the checks and add a positive assertion for failover
alongside the current usage/audit/guardrails validations.
🪄 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: de5bc026-9610-48a1-97d8-e83cd035e976

📥 Commits

Reviewing files that changed from the base of the PR and between 33e4d75 and 59dc99d.

📒 Files selected for processing (58)
  • .env.template
  • cmd/gomodel/docs/docs.go
  • config/config.example.yaml
  • config/config.go
  • config/config_test.go
  • config/failover.example.json
  • config/failover.go
  • docs/advanced/guardrails.mdx
  • docs/advanced/resilience.mdx
  • docs/advanced/workflows.mdx
  • docs/dev/possible-refactoring.md
  • docs/features/budgets.mdx
  • docs/features/failover.mdx
  • docs/features/user-path.mdx
  • docs/openapi.json
  • internal/admin/dashboard/static/js/modules/workflows-layout.test.cjs
  • internal/admin/dashboard/static/js/modules/workflows.js
  • internal/admin/dashboard/static/js/modules/workflows.test.cjs
  • internal/admin/dashboard/templates/page-workflows.html
  • internal/admin/handler_failover.go
  • internal/admin/handler_failover_test.go
  • internal/admin/handler_workflows_test.go
  • internal/app/app.go
  • internal/app/app_test.go
  • internal/auditlog/auditlog.go
  • internal/auditlog/auditlog_test.go
  • internal/auditlog/middleware.go
  • internal/core/context.go
  • internal/core/workflow.go
  • internal/failover/factory.go
  • internal/failover/resolver.go
  • internal/failover/resolver_test.go
  • internal/failover/service.go
  • internal/failover/service_test.go
  • internal/gateway/failover.go
  • internal/gateway/failover_test.go
  • internal/gateway/inference_execute.go
  • internal/gateway/inference_orchestrator.go
  • internal/gateway/inference_orchestrator_test.go
  • internal/gateway/interfaces.go
  • internal/gateway/refactor_findings_test.go
  • internal/live/broker_test.go
  • internal/responsecache/handle_request_test.go
  • internal/responsecache/simple.go
  • internal/server/failover_test.go
  • internal/server/handlers.go
  • internal/server/http.go
  • internal/server/internal_chat_completion_executor.go
  • internal/server/internal_chat_completion_executor_test.go
  • internal/server/messages_handler.go
  • internal/server/request_model_resolution.go
  • internal/server/translated_inference_service.go
  • internal/workflows/compiler_test.go
  • internal/workflows/types.go
  • internal/workflows/types_test.go
  • tests/e2e/release-e2e-scenarios.md
  • tests/integration/workflows_guardrails_test.go
  • tools/seed-demo-data.sh

Comment thread cmd/gomodel/docs/docs.go
Comment on lines +4613 to +4620
"admin.deleteFailoverRuleRequest": {
"type": "object",
"properties": {
"primary_model": {
"type": "string"
}
}
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Add required validation for primary_model.

Both request schemas publish primary_model as optional, but the handler rejects empty values. That breaks the published contract and can generate invalid clients. Add required: ["primary_model"] to both objects, then regenerate docs/openapi.json.

Also applies to: 4796-4812

🤖 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 `@cmd/gomodel/docs/docs.go` around lines 4613 - 4620, The request schemas for
admin.deleteFailoverRuleRequest (and the matching object at the other referenced
location) currently expose primary_model as optional even though the handler
requires it. Update the schema definitions in docs.go to mark primary_model as
required with required: ["primary_model"], then regenerate the OpenAPI output so
docs/openapi.json stays in sync.

</label>
<label class="workflow-feature-toggle" x-show="workflowFailoverVisible()">
<input type="checkbox" x-model="workflowForm.features.fallback">
<input type="checkbox" x-model="workflowForm.features.failover">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Associate this checkbox with a label.

Line 130 is still flagged as an unlabeled form control, which weakens keyboard/screen-reader usability and shrinks the click target. Wrap it in a <label> or add a matching id/for pair.

🧰 Tools
🪛 HTMLHint (1.9.2)

[warning] 130-130: No matching [ label ] tag found.

(input-requires-label)

🤖 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 `@internal/admin/dashboard/templates/page-workflows.html` at line 130, The
checkbox bound to workflowForm.features.failover is still unlabeled, so update
the workflow form markup to associate it with a visible label by either wrapping
the input in a label element or giving the input a unique id and matching it
with a for attribute; keep the change localized to the failover control in the
page-workflows template.

Source: Linters/SAST tools

Budget bool `json:"budget" bson:"budget"`
Guardrails bool `json:"guardrails" bson:"guardrails"`
Fallback bool `json:"fallback" bson:"fallback"`
Failover bool `json:"failover" bson:"failover"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve the legacy fallback audit field on reads.

Line 159 changes the persisted JSON/BSON key from fallback to failover. Existing audit documents written with workflow_features.fallback will now deserialize with Failover=false, so historical entries lose that flag in live preview and any other readers of WorkflowFeaturesSnapshot. Please add a backward-compatible read path or a data migration/backfill before switching the storage tags.

🤖 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 `@internal/auditlog/auditlog.go` at line 159, The `WorkflowFeaturesSnapshot`
field rename from `fallback` to `failover` breaks reads of existing audit
records because older documents still store `workflow_features.fallback`. Add a
backward-compatible deserialization path in `auditlog.go` so `Failover` is
populated from either legacy `fallback` or the new key, or perform a
migration/backfill before changing the BSON/JSON tags. Keep the fix localized
around `WorkflowFeaturesSnapshot` and its audit log read/write handling so
historical entries still surface the flag correctly.

Comment on lines +217 to 220
provider := &streamFailoverProvider{
streamsByModel: map[string]io.ReadCloser{
"fallback": io.NopCloser(strings.NewReader("data: {}\n\n")),
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Model the primary attempt as an empty stream, not a nil stream.

Line 292 returns nil for "primary" because the map only seeds "fallback", so this test is no longer exercising the “empty primary stream” path named by TestStreamResponsesFallsBackAfterEmptyPrimaryStream. Seed "primary" with an empty io.ReadCloser; otherwise a regression in empty-stream detection can slip by while a nil-stream path still passes.

Suggested change
  provider := &streamFailoverProvider{
  	streamsByModel: map[string]io.ReadCloser{
+ 		"primary":  io.NopCloser(strings.NewReader("")),
  		"fallback": io.NopCloser(strings.NewReader("data: {}\n\n")),
  	},
  }

As per coding guidelines, **/*_test.go: Add or update tests for behavior changes, and ensure tests cover request translation, response normalization, error handling, default configuration, and provider-specific parameter mapping.

Also applies to: 290-292

🤖 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 `@internal/gateway/refactor_findings_test.go` around lines 217 - 220, The test
setup for stream failover is seeding only "fallback", so the primary path in
streamFailoverProvider is still resolving to nil instead of an empty stream.
Update TestStreamResponsesFallsBackAfterEmptyPrimaryStream to explicitly seed
"primary" in streamsByModel with an empty io.ReadCloser so the test exercises
the empty-stream fallback behavior in streamFailoverProvider.streamForModel
rather than the nil-stream path.

Source: Coding guidelines

Comment thread tests/e2e/release-e2e-scenarios.md
Follow-up to the fallback->failover rename, from PR review:

- server/http.go: fix a `FailoverResolver` field comment that still said
  "fallback resolver".
- gateway/failover.go: rename the failover-callback closure param
  `fallback` -> `failoverFn` in executeWithFailoverResponse (it names the
  failover path, unlike the generic default-value `fallback` params which
  stay).
- admin/handler_workflows_test.go + tests/e2e/release-e2e-scenarios.md:
  assert the positive `failover` value (not just key presence) in the
  effective-features projection and payload, so a regression that dropped
  or misnormalized the renamed key is caught.

Deliberately not changed (out of scope / by design):
- No backward-compat shim for persisted `features.fallback` or the audit
  `workflow_features.fallback` key — an accepted breaking change for a
  brand-new feature (#444).
- The admin `/failover/generate` resolver already respects
  `failover.disabled_models` via the rule provider's Disabled(), so the
  synthetic `FailoverConfig{Enabled: true}` is not a bug.
- The workflow failover checkbox is wrapped in a `<label>` like every
  other toggle; the "unlabeled control" lint is a false positive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@SantiagoDePolonia

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews. Addressed in 12701561:

  • http.go — fixed a FailoverResolver field comment that still said "fallback resolver".
  • gateway/failover.go — renamed the failover-callback closure param fallbackfailoverFn (it names the failover path; the generic default-value fallback params elsewhere stay).
  • handler_workflows_test.go + release-e2e-scenarios.md — added positive-value assertions for the renamed failover key (not just key presence), so a regression that dropped/misnormalized it is caught.

Deliberately not changed:

  • Persisted-data back-compat (workflow features.fallback, audit workflow_features.fallback) — accepted breaking change for a brand-new feature (feat: add manual failover management #444), as noted in the PR description. No shim/migration.
  • /admin/failover/generate resolver "drops disabled models" — not a bug: the handler passes the failover Service as the rule provider, and effectiveDisabled() reads ruleProvider.Disabled(), which already includes config disabled_models (via ConfigRules). The synthetic FailoverConfig{Enabled: true} doesn't strip that state.
  • "Unlabeled failover checkbox" — the input is wrapped in <label class="workflow-feature-toggle"> exactly like every other feature toggle; the lint is a false positive on implicit label association.
  • refactor_findings_test primary/empty-stream seeding — pre-existing test setup unrelated to the rename.

@SantiagoDePolonia SantiagoDePolonia merged commit 1bde978 into main Jul 1, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants