# AGENTS.md

## Project: Mortgage Refix Decision Assistant

This repository contains a decision-support application for evaluating whether
it makes sense to refinance a mortgage **now** or **wait until the current
fixed-rate period expires**, and for comparing multiple new fixed-rate offers
with different durations and rates.

The application is not a generic amortization calculator. Its primary purpose is
to answer a practical decision question:

- Should the user switch now or wait?
- Which of multiple mortgages is rational to refinance first?
- Which new offer is best under current assumptions?
- What future interest rate is the break-even threshold where the decision
  changes?

The target user has one or more existing mortgages with low fixed rates that
will expire at different times, and receives one or more new bank offers (for
example, 3-year or 5-year fixed offers). The application should provide a fast,
clear, and user-friendly recommendation.

---

## Product intent

The product should optimize for **decision clarity**, not for exhaustive
mortgage administration.

The main screen should help the user answer, within a few seconds:

1. Does it make sense to refinance now?
2. For which mortgage does it make sense first?
3. Which offer dominates under current assumptions?
4. At what future rate does the decision flip?

The app should be opinionated and explicit. It should avoid presenting only raw
monthly payment numbers without interpretation.

---

## Implementation framework

This repository should be implemented with the frameworks that match the locally
available skills:

- **Deno 2.x** for runtime, tooling, formatting, linting, tasks, and TypeScript
  execution
- **Fresh 2.x** for the web application framework, routing, server rendering,
  and islands-based interactivity
- a **shared pure TypeScript domain layer** for all mortgage, comparison, and
  break-even logic

Do not steer the project toward Angular, NestJS, React SPA boilerplate, or
generic Node-first architecture. If backend behavior is needed, prefer
Fresh-native handlers and Deno-native capabilities unless there is a clearly
justified reason to introduce something else.

---

## Design and reference sources

There are two distinct source documents in this repository, and they serve
different roles:

- `DESIGN.md` is the **design source of truth**
- `example-code/single-page-mortgage-refixer.html` is an **algorithm and
  behavior reference only**

Design decisions must follow `DESIGN.md`. That includes visual direction,
typography, spacing, surface hierarchy, color usage, tonal layering, and the
"decision-first" presentation style.

The example HTML file may be used only to understand or validate:

- financial formulas
- comparison flow
- scenario structure
- recommendation logic
- break-even calculation behavior

Do **not** copy the example HTML file's visual design, layout, styling,
component structure, or UI patterns. The design must be derived from
`DESIGN.md`, not from the example implementation.

---

## Core business concept

The product compares strategies such as:

- **Wait until current fixation ends**
- **Switch now to a new fixed period**
- **Compare shorter vs longer fixed periods**

The application should support:

- one or more mortgages
- multiple offers
- scenario assumptions for future rates
- saved scenarios
- break-even rate analysis

The application should present both:

- a **quick approximation**
- an **exact amortized comparison**

---

## Financial logic

### 1. Exact mortgage payment formula

For annuity mortgages, monthly payment is:

M = P * i / (1 - (1 + i)^(-n))

Where:

- P = remaining principal
- i = monthly interest rate = annual rate / 12
- n = remaining number of months

### 2. Remaining balance after k months

B_k = P * (1 + i)^k - M * ((1 + i)^k - 1) / i

### 3. Interest paid over a period

Interest over a simulated period should be derived from amortized monthly cash
flows, not from a simple average rate approximation.

### 4. Break-even approximation for "switch now vs wait"

A simplified weighted-average formula may be shown in the UI as a fast
heuristic:

break_even_future_rate = ((new_months * new_rate) - (old_months * old_rate)) /
(new_months - old_months)

Interpretation:

- If the future rate at expiry is below this threshold, waiting is better.
- If the future rate at expiry is above this threshold, switching now is better.

This formula is only an approximation. It ignores amortization, fees, penalties,
and changing principal balance.

### 5. Break-even approximation for shorter vs longer offers

Example comparison between 3-year and 5-year fixed offers:

3 * rate_3y + 2 * future_rate = 5 * rate_5y

So:

future_rate = (5 * rate_5y - 3 * rate_3y) / 2

This is also a simplified approximation.

### 6. Exact decision logic

The production-grade decision engine should compare strategies using:

- full monthly amortization simulation
- total interest paid
- total cash paid
- fees and penalties
- optional NPV using a user-defined discount rate

The correct approach is:

1. simulate cash flows for each option
2. compare costs over the same horizon
3. numerically solve for the future rate where two strategies are equal

Root-finding via binary search is acceptable.

---

## Product requirements

### Required UX behavior

The app should:

- support at least two mortgages in one scenario
- support multiple offers with different rates and fix lengths
- show a recommendation in plain language
- show a comparison table with detailed numbers
- show break-even rate thresholds
- allow scenario saving/loading
- make it obvious which mortgage should be considered first
- help the user compare "wait" vs "switch now" immediately

### UX priorities

Priority order:

1. clear recommendation
2. break-even thresholds
3. offer comparison
4. detailed financial breakdown

Do not lead with raw tables. Lead with the decision.

### The UI should emphasize:

- “Refinance now” vs “Wait”
- “Mortgage A” vs “Mortgage B”
- “3-year offer” vs “5-year offer”
- “Break-even future rate”

---

## Scope boundaries

### In scope

- annuity mortgage modeling
- remaining principal and maturity
- current fixed rate and months until expiry
- multiple new offers
- exact amortized comparison
- simplified break-even heuristics
- scenario storage
- charts for cost vs future rate

### Out of scope for v1

- legal APR disclosures
- country-specific mortgage regulation logic
- insurance bundle pricing
- bank-specific penalties that require bespoke contract parsing
- offset accounts
- variable-rate products with nontrivial repricing rules
- tax effects
- inflation-adjusted real-cost analysis

These may be added later, but should not complicate the first usable version.

---

## Architecture expectations

### Preferred stack

- Deno 2.x runtime and tooling
- Fresh 2.x application framework
- shared pure TypeScript domain library for all financial calculations

### Framework guidance

Use Fresh conventions and primitives for application structure:

- `routes/` for pages and request handlers
- `islands/` for client-side interactive components
- `components/` for reusable presentational building blocks
- `static/` for static assets when needed
- shared domain modules for all financial logic outside UI code

Do not introduce framework layers that fight Fresh's model unless there is a
concrete, documented reason.

### Core rule

All mortgage and break-even logic must live in a pure, testable TypeScript
domain layer.

Do not bury financial logic in UI components.

### Suggested module structure

- `src/domain/mortgage/`
  - `types.ts`
  - `annuity.ts`
  - `simulation.ts`
  - `break-even.ts`
  - `recommendation.ts`

- `routes/`
  - `index.tsx`
  - scenario flows and result pages

- `islands/`
  - scenario editor
  - mortgage cards
  - offers editor
  - assumptions editor
  - recommendation interactions

- `components/`
  - recommendation summary
  - comparison table
  - break-even cards
  - charts
  - common UI primitives

- `src/shared/`
  - formatting
  - storage helpers
  - scenario serialization

### Domain modeling guidance

Use explicit types for:

- Mortgage
- Offer
- Scenario
- Assumptions
- SimulationResult
- ComparisonResult
- RecommendationResult

Avoid anonymous object blobs.

---

## Coding principles

### General

- Favor correctness over cleverness.
- Favor explicit types over implicit assumptions.
- Favor small pure functions.
- Keep formulas readable and documented.
- All numerical calculations should be deterministic and easy to test.

### Financial logic

- Use monthly periods as the default simulation unit.
- Avoid hidden approximations.
- If using a simplified formula, label it explicitly as approximate.
- Use the same comparison horizon for competing options.
- Separate heuristic formulas from exact amortization logic.

### UI

- The first visible output should be the recommendation.
- Use visual hierarchy to show:
  - best option
  - next-best option
  - cost delta
  - break-even threshold
- Do not overload the first screen with secondary detail.
- Follow `DESIGN.md` as the visual system of record.
- Prefer editorial hierarchy, tonal layering, and whitespace-based separation
  over generic dashboard chrome.
- Do not use the example HTML file as a design template.

### Persistence

- Local storage is acceptable for initial scenario persistence.
- Save structured scenario JSON.
- Keep storage schema versioned if possible.

---

## Recommendation engine behavior

For each mortgage:

1. simulate "wait until expiry"
2. simulate "switch now" for each offer
3. compare all options on equal horizon
4. rank by total cost and NPV
5. generate a human-readable recommendation

For multiple mortgages:

- compute recommendations independently
- then compute portfolio-level priority:
  - which mortgage has the strongest case for immediate action
  - which mortgage should likely be left unchanged for now

Recommendations should be short, explicit, and interpretable by a non-technical
user.

Example:

- “Refinance Mortgage A now; keep Mortgage B until expiry.”
- “Under current assumptions, both mortgages still favor waiting.”
- “The 5-year fix becomes better only if rates after 3 years exceed 4.04%.”

---

## Testing expectations

At minimum, include tests for:

### Unit tests

- monthly payment calculation
- remaining balance calculation
- cash flow simulation
- total interest calculation
- NPV calculation
- break-even solver
- recommendation ranking

### Scenario tests

Include realistic scenario tests such as:

- low current rate expiring in 2 months
- same low current rate expiring in 10 months
- same principal, same maturity, same offer
- verify that the shorter remaining current fix is more likely to justify
  switching now

### Regression tests

Protect against:

- negative balances
- invalid month counts
- incorrect horizon truncation
- inconsistent cost comparisons
- recommendation instability from rounding errors

---

## Decision philosophy

This project should not behave like a passive calculator.

It should behave like a decision assistant.

That means:

- identify the best option
- quantify the cost of the second-best option
- surface the break-even rate
- explain the recommendation in plain language

A correct but unreadable result is not sufficient. A beautiful but financially
wrong result is unacceptable.

---

## Milestones

### Milestone 1

- pure TypeScript domain engine
- annuity formulas
- wait vs switch-now comparison
- support two offers
- support two mortgages
- simplified recommendation text

### Milestone 2

- break-even solver for exact amortized comparisons
- chart: total cost vs future rate
- scenario saving/loading
- cleaner UI hierarchy

### Milestone 3

- compare arbitrary numbers of offers
- advanced assumptions
- fee and penalty modeling
- export/import scenario data
- more robust recommendation wording

---

## Non-goals for agents

Do not:

- turn this into a generic banking dashboard
- optimize prematurely for every mortgage product variation
- hide decision logic behind opaque abstractions
- prioritize animation or styling over financial clarity
- replace exact comparisons with only heuristic formulas

---

## Expected output quality from coding agents

When implementing or modifying this project:

- preserve clear business semantics
- keep formulas directly traceable to domain intent
- document assumptions
- prefer boring, maintainable code
- add tests for every financial rule change
- do not silently alter comparison methodology
- use Deno 2.x and Fresh 2.x as the default implementation framework
- align UI work with `DESIGN.md`
- treat `example-code/single-page-mortgage-refixer.html` as algorithm reference
  only

If a design or implementation choice would make the decision output less
trustworthy, reject it.

### Required verification

Before considering a task complete, always run:

- `deno task build`
- `deno fmt`
- `deno lint`

If any verification step fails, report it explicitly and do not claim the task
is fully complete.

### Browser verification with Chrome DevTools MCP

When working on UI, interaction, styling, runtime errors, or responsive layout,
also use the Chrome DevTools MCP against the local app.

Preferred workflow:

- open or reload `https://mortgage-refixer--local.st1c.deno.net/`
- inspect the console for runtime errors and browser issues
- inspect failed network requests when a resource does not load
- validate the affected UI at mobile and desktop widths
- use page snapshots for structure and accessibility-oriented inspection
- use screenshots only when visual confirmation is necessary
- after changing styles or client code, reload the page and verify the result
  again

Specific expectations:

- treat JavaScript exceptions, failed module loads, and repeated 4xx/5xx
  requests as real issues to investigate
- report console warnings that affect UX or accessibility, especially invalid
  form semantics or broken controls
- when checking responsive layout, verify both the mobile layout and a wider
  desktop layout instead of assuming CSS changes are safe
- if a task is specifically about a visible bug, confirm the fix in DevTools,
  not only by passing Deno build/lint/format checks

---

## Example user scenario

A user has:

- Mortgage A: current fixed rate 0.95%, expires in 2 months
- Mortgage B: current fixed rate 0.95%, expires in 10 months
- New offer: 3.31% fixed for 3 years
- Another offer: 3.60% fixed for 5 years

The application should quickly reveal:

- whether Mortgage A should be refinanced now
- whether Mortgage B should be left until expiry
- under what future rate assumptions that conclusion changes
- whether 3-year or 5-year offer dominates

This scenario should be part of tests, demos, and example data.

---

## Final instruction to agents

Build a tool that helps a user make a mortgage refix decision quickly and
correctly.

The main artifact is not the monthly payment. The main artifact is the
recommendation and the break-even threshold.

Keep the code testable, the formulas explicit, and the UI decision-first.
