# /html-report - Generate Application Tracker Dashboard Generate a self-contained HTML dashboard from `job_search_tracker.csv` and the application archives under `documents/applications/`. The output is a single `.html` file — no server, no dependencies — that can be opened directly in a browser. ## Step 0: Parse Arguments - No argument → output to `reports/application-dashboard.html` - A path argument (e.g. `/html-report ~/Desktop/report.html`) → use that path - `--open` flag → after writing, tell the user to open the file (cannot open a browser directly) Create `reports/` if it does not exist. --- ## Step 1: Collect Data Read in parallel: 1. **`job_search_tracker.csv`** — the primary source. Parse every row into a record with fields: `date`, `company`, `sector`, `role`, `role_type`, `channel`, `status`, `contact_person`, `fit_rating`, `notes`, `cv_file`, `cover_letter_file`, `source` 2. **`documents/applications/*/outcome.md`** — for each resolved application, read the outcome file to get the exact interview stages reached (the checkboxes) and any notes. Merge this into the matching tracker row by company+role fuzzy match (lowercase, ignore punctuation). If an archive exists for a row but there is no match, attach it as extra context anyway. Status normalisation — map tracker values to six canonical buckets before computing stats: - `drafted` → **Drafted** (documents written by `/apply`, not yet submitted) - `applied` → **Active** (resume submitted, no further signal) - `interview` → **Interview** - `offer` → **Offer** - `hired` → **Hired** - `rejected` / `no_response` / `no response` / `offer_declined` / `offer declined` / `withdrawn` → **Rejected/Closed** - anything else → **Rejected/Closed**, and name the unrecognised value once in the status breakdown — matching is case-insensitive The bucket map tolerates the legacy space spellings on read so nothing written before the canonical forms were locked drops out of the stats; the **Tracker status vocabulary** in `/outcome` is the authoritative set. --- ## Step 2: Compute Summary Stats From the normalised data compute: **Drafted rows are excluded from every statistic below** — they were never submitted. Report the Drafted count on its own, and include it only in the status breakdown. - **Total applications** - **By status bucket:** count per bucket - **By sector:** count per unique sector value - **By channel:** portal vs online vs referral vs other - **By year/season:** group by the `date` field (which may be a year like `2025` or a full date) - **Funnel rates:** what % progressed past resume screen (reached Interview or beyond) - **Rejection rate:** Rejected/Closed ÷ Total with a resolved status (exclude Active) --- ## Step 3: Generate the HTML Write a single self-contained HTML file. All CSS is inline in a `