M1: initial scaffold — pipeline, specs, source, Dockerfile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Paul O'Reilly
2026-04-17 23:52:40 +12:00
commit 8584def154
22 changed files with 1413 additions and 0 deletions

130
spec/report.md Normal file
View File

@@ -0,0 +1,130 @@
# Report Spec (WE-R)
## Overview
Assembles all collected data into a single Markdown file optimised for Claude
to read. Findings are phrased in a pre-interpreted tone: each issue explains
what it means and what to do about it, not just that a value is absent or wrong.
## Responsibilities
- Accept LighthouseResult[] and DomData as inputs
- Produce a single Markdown string
- Apply severity grading to Lighthouse scores
- Categorise findings as Critical (must fix) / Warning (should fix) / Info
- Write the report in the defined section order
- Deduplicate findings that appear in both desktop and mobile audits
Delegates to: nothing (pure function, no I/O)
## Dependencies
Read [lighthouse.md](lighthouse.md) for `LighthouseResult` shape.
Read [dom-analysis.md](dom-analysis.md) for `DomData` shape.
## Data Model
### Score grading
| Score | Grade | Colour label |
|---|---|---|
| 90100 | A | Good |
| 7589 | B | Needs improvement |
| 5074 | C | Poor |
| 049 | F | Critical |
### Severity rules
- **Critical (must fix):** Lighthouse finding with `score === 0` in any preset
- **Warning (should fix):** Lighthouse finding with `0 < score < 0.9` in any preset; or DOM issues (missing description, heading skips, missing alt)
- **Info:** Minor or informational items
## Requirements
**WE-R-1:** The report begins with a level-1 heading: `# Website Evaluation: {hostname}`.
Hostname is extracted from the URL (no scheme, no path).
**WE-R-2:** A generation line follows: `_Generated: {ISO date} · Audited: {full URL}_`
**WE-R-3:** Section order (all sections always present, even if empty):
1. Lighthouse Scores (desktop + mobile tables)
2. Core Web Vitals (desktop + mobile tables)
3. Critical Issues
4. Warnings
5. Info
6. Page Structure
7. Screenshots reference
8. Raw Data reference
**WE-R-4:** Lighthouse score tables have columns: Category | Desktop | Mobile | Desktop Grade | Mobile Grade.
Why: Side-by-side makes it easy to spot mobile regressions.
**WE-R-5:** Core Web Vitals tables have columns: Metric | Desktop | Mobile | Status.
Status is the worst of the two presets for that metric (e.g., if desktop is "good"
but mobile is "needs-improvement", show "needs-improvement").
**WE-R-6:** Each finding is rendered as a Markdown checkbox list item:
```
- [ ] **{title}** — {pre-interpreted explanation with fix guidance}
```
The title comes from `audit.title`. The explanation is built from `audit.description`
(stripped of Markdown links) plus any savings estimate.
**WE-R-7:** If the same audit ID appears as a finding in both desktop and mobile
results, it appears once in the report (not duplicated). The severity is the worst
across both presets.
**WE-R-8:** DOM issues (missing meta description, heading skips, missing alt text,
missing noopener) are included in the Warnings section with pre-interpreted phrasing.
Exact phrasing:
- Missing meta description: "Meta description missing — search engines will auto-generate one, often poorly. Add a 155-char summary."
- Heading skip: "Heading hierarchy skips {skip} — this confuses screen readers and weakens document structure."
- Missing alt: "{n} image(s) lack alt text — WCAG 1.1.1 violation (Level A); screen readers will skip them entirely."
- Missing noopener: "{n} link(s) open in a new tab without `rel=\"noopener\"` — the opened page can access `window.opener`."
**WE-R-9:** Page Structure section lists:
- Title (value + char count + assessment: "good" if 3065 chars, "too short" / "too long" otherwise)
- Meta description (value or "missing")
- Canonical (value or "not set")
- Heading counts per level (e.g., "H1×1, H2×4, H3×7")
- Images (total, missing alt count)
- Links (total, external, new-tab-no-opener count)
- Structured data (types detected or "none")
- Open Graph (present/absent)
**WE-R-10:** Screenshots section contains Markdown image references:
```markdown
![Desktop view](screenshots/desktop.png)
![Mobile view](screenshots/mobile.png)
```
**WE-R-11:** If all Critical, Warning, and Info sections are empty, replace each
with `_None detected._`.
**WE-R-12:** The function signature is:
```js
assembleReport(url, lighthouseResults, domData) string
```
It is a pure function — no file I/O, no side effects.
## Scenarios
### Scenario: Score table format
**Given:** Desktop performance 87, mobile performance 62
**When:** Report is assembled
**Then:** Score table shows desktop 87 (B), mobile 62 (C) in the same row
### Scenario: Finding deduplication
**Given:** `meta-description` audit fails (score 0) in both desktop and mobile results
**When:** Report is assembled
**Then:** The meta-description finding appears exactly once in Critical Issues
### Scenario: Empty sections
**Given:** All Lighthouse audits pass (score ≥ 0.9), no DOM issues
**When:** Report is assembled
**Then:** Critical Issues, Warnings, and Info sections each contain `_None detected._`
### Scenario: Title assessment
**Given:** Page title is 14 chars ("Example Domain")
**When:** Page Structure section is built
**Then:** Title line shows `"Example Domain" (14 chars — too short)`