Skip to content

feat(ui): add forecast champion selector foundation#359

Merged
w7-mgfcode merged 1 commit into
devfrom
feat/champion-selector-slice-a
Jun 1, 2026
Merged

feat(ui): add forecast champion selector foundation#359
w7-mgfcode merged 1 commit into
devfrom
feat/champion-selector-slice-a

Conversation

@w7-mgfcode
Copy link
Copy Markdown
Owner

Closes #356 · Slice A of 3 (A → B → C) of the Forecast Champion Selector.
PRP: PRPs/forecast-champion-selector-slice-a-selection-capability.md.

First interactive Champion Selector surface — configuration + availability
triage
. It deliberately stops before running the comparison (that is Slice B)
and before any training/prediction/promotion (Slice C).

Backend

  • GET /model-selection/modelsModelCatalogResponse — a backend-owned
    capability catalog (one entry per forecasting ModelType, 11 total), declared
    before GET /{selection_id} so Starlette matches the literal path (not a
    selection_id capture).
  • New pure module capabilities.py (build_model_catalog(), no DB/IO); each
    entry carries model_type, label, family (derived from
    forecasting.feature_metadata.model_family_for), feature_aware,
    requires_extra, default_params, supports_auto_predict, description,
    plus a default_candidate_model_types list.
    • feature_aware == the set whose forecasters set requires_features=True
      (random_forest/regression/lightgbm/xgboost/prophet_like) — i.e. exactly the
      set ForecastingService.predict() rejects.
    • requires_extra == {lightgbm, xgboost}; supports_auto_predict == not feature_aware.
    • default_params pinned from the live ModelConfig defaults.
  • ModelSelectionService.get_model_catalog() thin delegate; response schemas
    CandidateModelInfo + ModelCatalogResponse added to schemas.py.
  • No migration, no new mutation surface, no agent tool.

Frontend

  • /visualize/champion page shell (pages/visualize/champion.tsx) — lazy
    route + Visualize nav entry. Store → Product → Time period → Horizon → Models
    → Backtest settings, plus a live availability panel.
  • components/champion-selector/: searchable-entity-select (Popover +
    Input + filtered list — no new dependency), availability-panel
    (ready/limited/unusable + metrics + recommended split; not-enough-data empty
    state), backtest-settings-form (simple/advanced split, SplitConfig bounds,
    WAPE-default ranking metric with the locked tie-break + bias copy),
    candidate-model-picker (catalog-fed, family-grouped, opt-in-extra badges,
    cap 10), plus copy.ts / split-config.ts / run-request.ts helpers.
  • hooks/use-model-selection.ts: useModelCatalog + usePairAvailability
    (availability gated on a valid pair).
  • types/api.ts: full Model-Selection workflow contract added (Slice A
    consumes catalog/availability/SplitConfig only; the run/ranking/chart/forecast
    types are declared-for-later so Slices B/C inherit, not redefine).

Scope boundaries

  • "Run comparison" CTA is intentionally disabled with explanatory copy —
    Slice B wires the POST mutation. The page assembles a typed
    ModelSelectionRunRequest (with auto_train_winner:false / auto_predict:false
    pinned, both no-ops in the async path) but never sends it.
  • No Slice B/C behavior: no run/progress/cancel/results, no ranking charts,
    no winner explanation/business summary, no train/predict/override/promotion.
    Exactly one new backend route; no calls to any /{selection_id}* endpoint.

Validation

  • ruff check . + ruff format --check
  • pytest -m "not integration"1753 passed, 12 skipped (incl. 8 new
    test_capabilities + 2 new /models route/ordering tests)
  • pnpm tsc --noEmit ✅ · pnpm lint ✅ (exit 0; only a pre-existing TanStack
    Table warning remains) · pnpm test --run282 passed (28 new Champion tests)
  • pyright app/features/model_selection ✅ 0 errors

Known pre-existing (NOT introduced here)

  • mypy app/ / pyright app/ report lightgbm/xgboost optional-dep
    import-resolution errors in forecasting/ + registry/ (CI installs the
    extras). None are in model_selection; documented in the PRP's Level-4 notes.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Sorry @w7-mgfcode, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c5709f3-8095-47bf-be96-578218909aa1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/champion-selector-slice-a

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 and usage tips.

@w7-mgfcode w7-mgfcode merged commit e7f4db7 into dev Jun 1, 2026
7 of 8 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.

1 participant