ArvexiBuilders Blog

What-If Analysis Without the Spreadsheet

A controller at a major restaurant chain told us about his modification workflow. Every potential lease change required building a new Excel model from scratch: copy the amortization schedule, manually adjust the terms, recalculate PV, check the classification, compare to the original. For a single modification, he might build five or six versions before finding the right structure.

"We'd spend half a day just modeling different scenarios. What if we extend? What if we reduce rent? What if we exit early? Each one is a separate spreadsheet."

We built a system where the controller just asks.


The architecture

The scenario engine has two layers. The conversational layer handles intent parsing and tool routing. The calculation layer runs the actual ASC 842 math: load the lease, merge overrides, generate a full amortization schedule, summarize, compute deltas.

Architecture
Two-Layer Scenario Engine
Conversational Layer
User Prompt
AI Agent
model_scenario | reverse_scenario
Agent Response
tool call
results
Calculation Layer
loadLease()
const lease = await prisma.lease.findUnique({ include: { scheduleEntries: true } })
buildParams()
{ ...currentParams, ...scenarioOverrides }
generateSchedule()
PV → amortization → interest → classification
summarize()
computeDelta()
{ metric, current, scenario, delta, favorable }
Click any node for implementation details · Left: what the user sees · Right: what runs under the hood

The key architectural decision: scenarios are entirely read-only. The agent calls model_scenario, which runs the full calculation pipeline in memory without persisting anything. Nothing touches the database until the user explicitly commits. This means controllers can explore freely without fear of corrupting production data.


Forward and reverse

The system supports two fundamentally different modes. Forward modeling: "What if we extend to 10 years?" Change the inputs, see the outputs. Reverse modeling: "What term gives us $50K/month?" Set the target, find the inputs.

Two Modes
Forward vs. Reverse Scenario Modeling
user "What if we extend lease LS-2024-0412 to 10 years at $55K/month?"
User changes
Term → 10 years, Rent → $55K/mo
model_scenario
Merge overrides with current lease
generateSchedule()
Full ASC 842 recalculation
Result
Liability: $4.8M (+$2.1M) · Expense: $47.2K/mo (-$8K)
Forward: One call to model_scenario. Merges the user's changes with current lease data, runs the full ASC 842 calculation, and returns a before/after comparison. Read-only — nothing is persisted until the user commits.
Toggle between modes · Forward = one calculation · Reverse = bisection loop wrapping forward

Forward is a single call to model_scenario. Reverse wraps it in a bisection loop. The system detects whether the relationship is normal or inverted (longer term means lower monthly payment, which is inverted), sets initial bounds, and halves the search space each iteration:

export async function reverseScenario(input: ReverseInput): Promise<ReverseResult> {
  let low = input.lowerBound;
  let high = input.upperBound;
 
  for (let i = 0; i < MAX_ITERATIONS; i++) {
    const mid = Math.round((low + high) / 2);
    const result = await modelScenario({ ...input.base, [input.variable]: mid });
    const error = Math.abs(result[input.targetMetric] - input.targetValue) / input.targetValue;
 
    if (error <= TOLERANCE) return { converged: true, value: mid, iterations: i + 1 };
 
    const overshoot = result[input.targetMetric] > input.targetValue;
    if (input.inverted ? overshoot : !overshoot) low = mid;
    else high = mid;
  }
  return { converged: false };
}

Bisection in action

Watch the algorithm converge. Pick a target monthly expense for an $8M lease at 4.5%, and the bisection search finds the lease term that achieves it, narrowing the bounds by half each iteration.

Reverse Modeling
Bisection Search — Find the Term
Target monthly expense:
$8M lease · 4.5% rate · 1% tolerance
12 moLease Term (months)360 mo
1260120180240300360
#
Low
High
Mid
Result
Error
Click "Run Search" to start
Target
$50,000/mo
Found Term
Actual Expense
Final Error
Iterations
Pick a target expense and click "Run Search" · Watch bisection narrow the bounds

The feasibility classification matters: if no term between 12 and 360 months can hit the target, the agent says so rather than returning a nonsensical answer. Integer rounding is handled separately: lease terms must be whole months, so the final answer snaps to the nearest integer and re-verifies.


The impact

Controllers used to spend half a day per modification exploring scenarios. Now they ask in natural language and get structured before/after comparisons in seconds.

Impact Analysis
Scenario Delta Visualization
Current
decrease
Metric
increase
Scenario
$2.7M
Lease Liability
+$2.1M
$4.8M
$2.4M
ROU Asset
+$1.9M
$4.3M
$55K
$-8K
Monthly Expense
$47K
$3.3M
Total Remaining Payments
+$2.4M
$5.7M
60 mo
Remaining Term
+60 mo
120 mo
Toggle between 3 scenarios · Bars show direction and magnitude of change · Red = unfavorable, Green = favorable

The modification service behind the scenes uses decimal.js for all monetary math: PV recalculation, gain/loss computation, SSP penalty allocation. When the user commits a scenario, the service generates journal entries, updates the amortization schedule, and records the full before/after state for the audit trail.

Every scenario the controller explored is preserved in the conversation history. The auditor can see not just what was committed, but what alternatives were considered and why they were rejected.


See Intelligence and Lease Accounting to learn more about these capabilities.

Previous: Roll Forwards That Generate Themselves