# Taux Immo Fr

> Research French mortgage rates → offline weekly HTML market report: bank averages, scenario matrix, leading indicators, sourced forecasts. No personal data. Don't use for a personal capacity calc (use capacite-emprunt-fr) or non-France loans.

- Skill: `luongnv89/taux-immo-fr` (Agent Skill, multi-file: 15 files)
- Install (CLI): `npx skillmds@latest add luongnv89/taux-immo-fr`
- Raw SKILL.md: https://api.skillmd.com/api/skills/luongnv89/taux-immo-fr/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: luongnv89 (https://skillmd.com/u/luongnv89)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/luongnv89/taux-immo-fr

---


# taux-immo-fr — Weekly French mortgage-rate market report

Produce a single self-contained HTML **weekly briefing** on the French home-loan
market: where rates are, who offers what, what the leading indicators say about
next week, and what named analysts predict. It is a **market panorama, independent
of any personal situation** — the user supplies nothing about themselves.

## When this skill triggers

- "Quel est l'état du marché des taux immobiliers en France ?"
- "Fais-moi le rapport hebdo des taux de crédit immobilier."
- "Quels taux proposent les banques en ce moment / cette semaine ?"
- "Compare les taux entre banques et courtiers."
- "Quelles sont les prévisions de taux immobilier ?"
- "Génère le rapport HTML taux immobilier" / user invokes `/taux-immo-fr`.

Do **not** trigger for — route elsewhere:

- **"Combien je peux emprunter ?" / capacité d'emprunt / "avec mes revenus…"** →
  that is **capacite-emprunt-fr** (personal, HCSF calc). This skill never asks for
  the user's income, apport, or charges.
- Crédit à la consommation, rachat/regroupement de crédits, prêt pro.
- Taux hors France (Belgique, Suisse, Luxembourg).
- Assurance emprunteur deep-dives, or a specific loan offer review.

## Output: one self-contained offline HTML file

The skill writes **a single `.html`** to the user's current working directory:

```
./taux-immo-fr-<week>.html      e.g. ./taux-immo-fr-2026-S24.html
```

That file:

- Renders with **vendored, inlined** TailwindCSS (compiled, shadcn/ui design
  tokens) and **vendored Chart.js** — **zero external requests, opens fully
  offline**. No CDN, no build, no server.
- Shows: a fail-loud provenance banner, hero KPI + week-over-week delta, a
  leading-indicators strip (OAT 10Y / TEC10 / ECB), a 12-month trend chart, a
  **scenario matrix** (borrower profile × duration), a per-network comparison
  chart, editorial cards from lesser-known institutional sources, named-and-dated
  forecast cards, and a méthodologie/sources section.
- Embeds the full structured data as JSON in `<script id="report-data">` — the
  chart source AND the audit trail (see `references/data-schema.md`).

There is no separate markdown summary; the HTML is the deliverable.

## The integrity contract (read first — this is the whole point)

This skill reports **current numbers** and **attributed predictions**. A polished
report makes a fabricated rate look authoritative. So:

1. **Every rate carries a source + fetch date** in the JSON block. No number
   without provenance.
2. **No forecast without a real fetched quote.** Never paraphrase "a BNP economist
   expects 3% by Q4" from memory. No quote → the forecast card is left in its
   dashed empty state with "pas de citation vérifiable cette semaine".
3. **Fail loud.** When a source 403s or times out, the report SAYS so in the banner
   and the méthodologie list — it never silently fills the gap with a guess. A
   visibly stale-but-honest report beats a fresh-looking fabricated one.
4. **Institutional insight cards shrink when sources fail** — an absent card beats
   an invented "durée moyenne 248 mois".

`references/sources.md` documents which French sources actually fetch (Empruntis,
ECB, Trading Economics) and which 403 behind Cloudflare (banque-france.fr,
aft.gouv.fr, meilleurtaux.com sometimes), with per-source extraction prompts and
flagged-stale fallback values.

## Prerequisites

- `python3` on PATH (for `scripts/build_report.py` — stdlib only, no pip installs).
- WebFetch access. Several sources 403; the skill degrades gracefully per
  `references/sources.md`.
- The **vendored assets must exist**: `assets/report.css`, `assets/chart.umd.min.js`,
  `assets/report.boot.js`. They ship with the skill. Only re-run
  `scripts/build_assets.sh` (network + Tailwind CLI) if you change the template.

## Workflow

Five phases, in order. End each with a one-line status before moving on.

### Phase 1 — Research the market (fetch, in parallel)

Fetch the four source clusters from `references/sources.md` **in parallel**.
Record fetch status (OK / 403 / fallback) and `as_of` for each — this populates
the provenance banner. The four clusters:

1. **Mainstream barèmes** (Empruntis ✅, Pretto, Meilleurtaux⚠) — monthly averages
   for the matrix + comparison.
2. **Leading indicators** (OAT 10Y via Trading Economics ✅, ECB key rates ✅) —
   the weekly-moving story; **always fetch these**, they're what make a *weekly*
   report non-redundant.
3. **Institutional / lesser-known** (Crédit Logement/CSA, Banque de France
   production) — the editorial insight cards.
4. **Forecasts** (named courtier economists, bank notes, press) — sourced quotes
   only.

> **Optional fan-out:** for a thorough weekly sweep you may spawn one research
> subagent per cluster (Agent tool) so the main context stays clean — each returns
> structured findings you assemble in Phase 2. Default is inline parallel WebFetch.
> Either way, the integrity contract holds: a subagent that couldn't fetch a source
> reports that, and you record it as unavailable — you do not let it improvise.

### Phase 2 — Assemble the data object (with provenance)

Build the JSON object matching `references/data-schema.md`. **Copy
`references/sample-data.json` as your starting template** and replace its values
with what you fetched — it has the exact shape and the right units already. For
**every** field, attach the source + `as_of`. Compute the derived
`spread_oat_bareme_pt`. Set `provenance.sources_ok` / `sources_total` to the
**honest** counts and list every failed source in `provenance.unavailable` +
`methodology_sources`. Forecasts and insights arrays contain **only** what you
actually fetched.

**Units:** put every rate in as a **percent number** (`3.45` = 3.45 %), scalars and
chart arrays alike — see the unit rule in the schema. Deltas are points
(`avg_20y_delta_pt`) / basis points (`oat_10y_delta_bps`), signed.

Write the object to a temp file, e.g. `/tmp/taux-immo-data.json`.

### Phase 3 — Build the self-contained file (the JSON is the only thing you author)

You do **not** hand-edit HTML. `scripts/build_report.py` renders **both** the
charts **and** every text section (hero, indicator tiles, matrix rows, insight
cards, forecast cards, banner counts, méthodologie list) from your JSON, then
inlines the vendored CSS + Chart.js + boot script into one offline HTML. This is
deliberate: it makes "the number in the prose == the number in the JSON" a
mechanical guarantee instead of a hope, and lets the script **fail loud** rather
than silently ship prototype/empty data.

```bash
python3 scripts/build_report.py \
  --data /tmp/taux-immo-data.json \
  --out ./taux-immo-fr-<week>.html
```

The script **refuses to write** (non-zero exit) if:

- a required section (`headline`, `indicators`, `matrix`, `provenance`,
  `methodology_sources`) is missing — so an incomplete fetch can't produce a
  half-empty report;
- any `{{placeholder}}` would survive unfilled — so a forgotten field is a hard
  error, not a fabricated-looking page;
- a vendored asset is missing.

Empty `insights` / `forecasts` arrays are **allowed** and render as explicit
honest empty-state cards ("aucune citation vérifiable cette semaine") — never as
invented content. FR formatting (decimal comma "3,42 %", arrows on deltas) is
handled by the renderers; you only supply correct numbers in the JSON.

### Phase 4 — Verify offline + report

Confirm the output is genuinely self-contained before handing it over:

```bash
grep -niE '<(script|link|img|source)[^>]*(src|href)=["'"'"']https?://' ./taux-immo-fr-<week>.html \
  && echo "FAIL: external load found" || echo "PASS: fully offline"
```

Tell the user the file path, the headline rate + week-over-week move, how many
sources were verified vs. unavailable, and that the file opens offline. Then emit
the Step Completion Report below.

## Acceptance Criteria

The run is done when **all** hold (verify each before reporting PASS):

- A single `./taux-immo-fr-<week>.html` is written; `build_report.py` exited 0.
- The offline grep in Phase 4 prints `PASS: fully offline` (0 external loads).
- Every rate in `<script id="report-data">` has a `source` + `as_of`; `provenance.sources_ok/sources_total` match the honest fetch counts.
- No `{{placeholder}}` survives in the output (the build asserts this).
- `forecasts`/`insights` contain only fetched, attributed items — or render the explicit honest empty state, never invented content.

## Expected output

A self-contained HTML report plus the terminal Step Completion Report shown below — e.g. `Sources fetched: √ 6/8`, `Self-contained offline: √ grep clean`, `Result: PASS`. The HTML file opens in a browser with charts rendered and zero network requests. See the Step Completion Report block at the end for the exact shape.

## Edge Cases

Inputs the skill rejects or handles specially:

- **WebFetch 403 / timeout** — retry once with the secondary URL in `sources.md`.
  Still failing → use the flagged-stale fallback, set `source_fallback: true`, and
  surface it in the banner. Never present a fallback as a fresh fetch.
- **No forecast quotes found** — ship empty forecast cards, do not invent. This is
  correct behavior, not a failure.
- **`build_report.py` non-zero exit** — read stderr; it names the missing asset or
  placeholder. Fix that input; never hand-edit the output to paper over it.
- **External-ref warning from the script** — an asset was linked instead of
  inlined. Inline it; the offline guarantee is non-negotiable.
- **Charts blank in browser** — almost always a unit mismatch in `trend`/`banks`
  (decimal vs percent). Make both percent. See the schema unit rule.
- **"rate field outside the sane percent band" exit** — you passed a decimal
  (`0.0345`) or basis points (`345`) where a percent number (`3.45`) was required.
  Fix the unit in the JSON; the guard exists so a 100×-wrong rate never ships.

## Step Completion Report

```
◆ Weekly Taux-Immo Report (FR market panorama)
··································································
  Sources fetched:        √ N/M (list any 403/fallback)
  Provenance complete:    √ every rate has source + as_of
  Forecasts sourced:      √ all named+dated (or empty, none invented)
  Self-contained offline: √ grep clean — 0 external loads
  Report written:         √ ./taux-immo-fr-<week>.html
  ____________________________
  Result:                 PASS | PARTIAL (stale sources) | FAIL
```

## Reference files

- `references/sources.md` — the four source clusters, working vs 403 URLs,
  extraction prompts, flagged-stale fallbacks, the integrity contract.
- `references/data-schema.md` — the auditable JSON block; unit rule; provenance.
- `references/sample-data.json` — a complete, correctly-shaped example to copy and
  adapt (right keys, right units). Start here in Phase 2.
- `references/html-template.html` — the static report shell. Asset markers
  (`/*__REPORT_CSS__*/`, `/*__CHART_JS__*/`, `/*__REPORT_BOOT__*/`,
  `<!--__REPORT_DATA__-->`) + `{{TOKEN}}` text placeholders the script fills from JSON.
- `scripts/build_report.py` — renders all text + charts from JSON and inlines
  vendored assets → offline HTML (runtime). Fails loud on missing data / unfilled tokens.
- `scripts/build_assets.sh` — re-vendors assets (build-time only; network + Tailwind CLI).

