feat(frontend/recs): On-Demand Monthly column should use provider on_demand_cost directly (no reconstruction) · Issue #330 · LeanerCloud/cloud-commitments-cli · GitHub
Skip to content

feat(frontend/recs): On-Demand Monthly column should use provider on_demand_cost directly (no reconstruction) #330

Closed

Description

@cristim

Summary

Change onDemandMonthly() in frontend/src/recommendations.ts to return the
provider-supplied r.on_demand_cost directly instead of reconstructing it from
monthly_cost + savings + (upfront_cost / months_in_term). When
on_demand_cost is missing or zero, render — instead of falling back to a
reconstructed value.

Why

PR #322 deliberately scoped the On-Demand Monthly column to a
reconstruction-only helper (its commit comment says: "It does NOT use
on_demand_cost even when available, because the column's purpose is to display
the reconstructed denominator so users can verify the formula against the raw
fields visible in the same row"
).

In practice, post-#277 / #312 / #324 every supported provider (AWS, Azure, GCP)
plumbs on_demand_cost through to the frontend, and effectiveSavingsPct()
already prefers the provider value when present (see
frontend/src/recommendations.ts:738-756 — hasOnDemand branch). The two
columns therefore disagree for any row where the formula reconstruction drifts
from the provider's billed on-demand price (rounding, partial-day proration,
provider-specific list price quirks, AWS On-Demand Capacity Reservations, etc.).

The product decision: surface the authoritative number from the provider,
not a teaching-tool reconstruction. Users who want to verify the formula can
still see monthly_cost + savings + upfront_cost/term in the row's other
columns and do the arithmetic themselves; making On-Demand Monthly match
effectiveSavingsPct's denominator is more important than making it
reconstructible.

Scope

  • onDemandMonthly(): drop the reconstruction. Return r.on_demand_cost when
    it's a positive number; return null otherwise (so the cell shows —,
    consistent with the existing monthly_cost == null em-dash rendering).
  • Update the doc comment on onDemandMonthly() to reflect the new contract.
  • Update the unit tests in frontend/src/__tests__/recommendations.test.ts:
    • keep "prefer explicit on_demand_cost",
    • replace "fall back to reconstruction" tests with "return null when
      on_demand_cost is null/missing/zero",
    • the existing reconstruction-arithmetic tests can be deleted — they're
      asserting behaviour we're explicitly removing.

Out of scope

Acceptance criteria

  • onDemandMonthly() returns r.on_demand_cost when it's > 0.
  • Returns null when r.on_demand_cost is null, undefined, or 0.
  • Column cell renders — for null cases (same as monthly_cost-null today).
  • Sort + numeric filter on on_demand_monthly still treat null as bottom
    (POSITIVE_INFINITY for sort, NaN for filter — already in place).
  • Unit tests added/updated; full test suite green.
  • No regressions in existing column ordering / sort / filter tests.

Related

Activity

  1. cristim commented on May 13, 2026

    @cristim
    MemberAuthor

    Verified resolved by PR #331 (merged 2026-05-11).

    • AC1 – returns r.on_demand_cost when > 0: onDemandMonthly now contains exactly if (r.on_demand_cost != null && r.on_demand_cost > 0) { return r.on_demand_cost; } — the arithmetic reconstruction is gone. Test: "returns provider on_demand_cost directly when populated" asserts odm === 500 with all supporting fields set to different values.
    • AC2 – returns null for null, undefined, or 0: Three dedicated unit tests cover each case explicitly; the delete r.on_demand_cost (missing-field) case is also covered.
    • AC3 – cell renders — for null cases: Column-rendering test "On-Demand Monthly column renders em-dash when on_demand_cost is missing" confirms the em-dash path still fires; the fixture now omits on_demand_cost rather than using monthly_cost: null, which is the correct null trigger post-feat(frontend/recs): On-Demand Monthly column should use provider on_demand_cost directly (no reconstruction) #330.
    • AC4 – sort null-last / numeric-filter NaN sentinel unchanged: Sort comparator comment and SORTABLE_NUMERIC_COLUMNS entry are unchanged in logic (?? Number.POSITIVE_INFINITY still present); numericCellValue still returns Number.NaN for null. Filter regression test updated to trigger null via missing on_demand_cost and still asserts the row does not match = 0.
    • AC5 – unit tests updated; full suite green: 7 reconstruction-arithmetic tests replaced with 5 new-contract tests; 3 column-rendering tests updated; PR reports 1578 passed / 0 failed across 42 suites.
    • AC6 – no regressions in column ordering / sort / filter tests: effectiveSavingsPct fallback logic untouched; sort/filter column tests updated to align fixtures with new null condition and all pass.

    Recommending closure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions