feat(040): H2 forecast presets + Claude billing cadence toggle#120
Conversation
Replace the Budget / Cost Forecast presets (Conservative / Expected / Aggressive) with three H2 2026 migration scenarios — H2 Gradual / H2 Plan (default) / H2 Accelerated — encoding the Console API fade-out, the Claude seat ramp (+10/+15/+20 per period at 35-40% Premium), a very slow Copilot fade, and flat Cursor / MS Copilot. Add a Monthly / Yearly billing toggle to the Claude seats line: yearly commitment prices seats at $20 std / $100 prem effective per month (CLAUDE_YEARLY_FACTOR = 0.8 on the live tier prices, applied inside the rounding). The cadence is a procurement assumption, not a scenario lever: it persists across preset switches and is threaded into preset scoring so tiles, ghost lines, and comparison rows stay like-for-like; reset restores monthly. Engine + client + unit tests only — no schema or loader changes. New hand-computed regression anchors on the frozen fixture; billing-factor coverage incl. a round-placement pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- currentMonthlyCost claudeSeats branch delegates to toolCostAt(tool, p, 0) instead of duplicating the blend + billing-factor math - centralize the cadence->factor mapping as exported claudeBillingFactor() - claudeBilling: useMemo -> plain derived const (primitive value) - BillingToggle takes the tool whole instead of three projected props - drop redundant Math.round before formatUSD0; footnote computes the discount % from CLAUDE_YEARLY_FACTOR instead of hardcoding 20% 623 unit tests, lint, typecheck, and the 24-check browser run all green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
Updates the Budget / Cost Forecast Simulation (spec 036) to reflect the org’s H2 2026 plan by replacing the legacy Conservative/Expected/Aggressive presets with three H2 migration presets, and adds a Monthly/Yearly billing cadence toggle that discounts only the Claude seats line (via a 0.8 factor) while persisting across preset switches.
Changes:
- Replace forecast presets with
h2Gradual,h2Plan(default), andh2Accelerated, including updated unit-test anchors. - Add Claude seats billing cadence (
monthly/yearly) to the engine and thread it through preset application/scoring and the client UI. - Add spec documentation (implementation plan/notes) and update
CLAUDE.md“Recent Changes”.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/scenarios/budget-forecast.test.ts | Updates preset expectations and adds billing-factor coverage + regression anchors. |
| src/lib/scenarios/budget-forecast.ts | Introduces H2 preset keys/values and Claude seats billing factor support in projection math and preset input stamping. |
| src/app/scenarios/budget-forecast/budget-forecast-client.tsx | Re-keys UI to new presets and adds a Claude billing toggle that persists across preset switches and affects scoring. |
| specs/040-h2-forecast-presets/implementation-plan.html | Adds the written implementation plan/spec for the change set. |
| specs/040-h2-forecast-presets/implementation-notes.html | Adds running notes/tradeoffs/verification record for the implementation. |
| CLAUDE.md | Documents spec 040 in the repository “Recent Changes” section. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <BillingToggle | ||
| tool={tool} | ||
| billing={params.billing ?? "monthly"} | ||
| onChange={(billing) => onChange({ billing })} | ||
| /> |
There was a problem hiding this comment.
Fixed in c3fff2b — the control call site now normalizes monthly back to undefined (billing: billing === "yearly" ? "yearly" : undefined), preserving the "undefined ≡ monthly" rule, and the toggle guards no-op clicks so the already-active option no longer mutates state. Same normalization pattern as the burn-cap slider's "No cap" → undefined.
| <ToggleButton | ||
| active={billing === "monthly"} | ||
| onClick={() => onChange("monthly")} | ||
| > |
There was a problem hiding this comment.
Fixed in c3fff2b — onClick now guards on a cadence change (billing !== "monthly" && onChange(...)), so clicking the active Monthly is a no-op and the plan stays on its preset. Added a browser regression check for exactly this (no-op Monthly click keeps the H2 Plan tile active).
| <p className="mt-1.5 font-mono text-[10px] uppercase tracking-wide text-faint"> | ||
| {formatUSD0((tool.stdPrice ?? 0) * factor)} std ·{" "} | ||
| {formatUSD0((tool.premPrice ?? 0) * factor)} prem / seat / mo | ||
| </p> |
There was a problem hiding this comment.
Fixed in c3fff2b — restored Math.round at the display boundary with a comment noting formatUSD0's integer-cents contract. Agreed that relying on Intl's float rounding would be fragile if a discounted tier price ever lands on fractional cents.
- guard no-op billing-toggle clicks so clicking the active cadence doesn't mutate state / mark the plan Custom - normalize monthly back to undefined at the control call site (undefined === monthly, keeps params deep-equal to pristine presets) - round discounted tier prices to integer cents before formatUSD0 (its documented contract) instead of relying on Intl float rounding Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…5 keys User review of the open questions: ~5 remaining Console keys at FY-end is the realistic 'a few applications and a few users' target (not ~2), and the monthly billing default stays. - h2Gradual api: compound -25% -> -15%/period (~13-14 keys from 42 over 7) - h2Plan api: compound -35% -> -25%/period (~5-6 keys) - h2Accelerated unchanged at -50% (~0.3 keys) Unit anchors recomputed by hand: 719066 / 892744 / 1028750 monthly, 774184 h2Plan yearly; strict ascending ordering preserved. Spec plan and notes updated; open questions marked resolved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Post-review tuning per the author's answers to the two open questions in the implementation notes (e2e2b7f):
All checks re-run green (623 unit tests, lint, typecheck). |
Summary
Evolves the Budget / Cost Forecast Simulation (spec 036) to model the org's actual H2 2026 plan, replacing the three generic presets, and adds a Monthly / Yearly billing toggle for the Claude seats line.
Presets (replace Conservative / Expected / Aggressive)
Compound decline encodes "fade out very fast, down to almost zero — a few applications and a few users remain": from today's 42 active keys over the ~7 remaining FY2026 periods, the central plan lands at ≈2 keys. Rationale is documented in the engine's preset doc comment and the spec folder.
Billing toggle
CLAUDE_YEARLY_FACTOR = 0.8on the loader-supplied prices (exact against the live tiers, and it keeps tracking them if they're edited).Scope
Engine + client + unit tests only — no schema or loader changes, feature stays read-only. Spec folder:
specs/040-h2-forecast-presets/(implementation plan — adversarially challenged before coding — and running implementation notes incl. design decisions / tradeoffs / open questions).Verification
currentMonthlyCostdelegates totoolCostAt(·, ·, 0)for claudeSeats, centralizedclaudeBillingFactor(), prop/memos tightened🤖 Generated with Claude Code