Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ jobs:
restore-keys: |
${{ runner.os }}-social-images-

# No version here on purpose: action-setup reads package.json's
# packageManager field, so CI and local development cannot drift.
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22.12.0
node-version-file: .nvmrc
cache: pnpm

- name: Install dependencies
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ docs/materials/*

# graphify knowledge-graph cache (regenerate with `graphify update .`)
graphify-out/

# local Lighthouse output (pnpm run lighthouse / audit-performance)
reports/
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22.12.0
52 changes: 45 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,40 @@ English is the listing language. Spanish is a twin: language toggle, title searc
- Tailwind CSS
- [Sharp](https://sharp.pixelplumbing.com/) — used by the social-image step to resize AVIF sources to JPEG/PNG

## Toolchain

| Tool | Version | Source of truth |
| ------- | --------------------- | ------------------------------------------ |
| Node.js | 22.12.0 (`>=22.12.0`) | `.nvmrc`, `engines.node` in `package.json` |
| pnpm | 12.3.4 | `packageManager` in `package.json` |

CI reads both from those files — `actions/setup-node` uses `node-version-file: .nvmrc`
and `pnpm/action-setup` uses the `packageManager` field — so there is no separate
version to keep in sync in `.github/workflows/ci.yml`.

Locally, `corepack enable` makes `pnpm` in this directory resolve to the pinned
version. Without corepack, install pnpm 12 yourself; older majors may not
understand every key in `pnpm-workspace.yaml`.

`pnpm-workspace.yaml` also carries dependency `overrides` (tar, minimatch,
fast-xml-parser, ajv, rollup, devalue). They are deliberate pins, not leftovers —
review them against a fresh advisory check before changing or removing any.

## Quick start

```bash
git clone https://github.com/antoniwan/notes.git
cd notes
pnpm install
corepack enable
pnpm install --frozen-lockfile
pnpm run dev
```

Then open `http://localhost:4321`.

This repo expects **pnpm**; you can use npm or yarn if you change commands yourself.
`--frozen-lockfile` is what CI runs; use it locally too unless you are
deliberately changing dependencies. This repo expects **pnpm**; you can use npm
or yarn if you change commands yourself.

## Environment variables (optional)

Expand All @@ -75,7 +97,8 @@ Remark42 uses `PUBLIC_REMARK42_HOST` and `PUBLIC_REMARK42_SITE_ID` when you turn
| ----------------------------------- | --------------------------------------------------------------------------------------------- |
| `pnpm run dev` | Dev server |
| `pnpm run build` | Builds social JPG/PNG from AVIF (skipped when fingerprints + files match), then `astro build` |
| `pnpm run preview` | Serves the production build locally |
| `pnpm run preview` | Serves prerendered `dist/client` on :4321 (no Vercel CLI needed; see caveat below) |
| `pnpm run preview:vercel` | `astro preview` — needs the Vercel CLI installed |
| `pnpm test` | Vitest unit tests (publish filters, SEO routing, feed HTML, quotes helpers) |
| `pnpm run test:watch` | Vitest in watch mode |
| `pnpm changelog:since` | Commits + file groups since the previous version (for CHANGELOG drafts) |
Expand All @@ -91,13 +114,28 @@ Remark42 uses `PUBLIC_REMARK42_HOST` and `PUBLIC_REMARK42_SITE_ID` when you turn
| `pnpm run generate-favicons` | Favicon assets |
| `pnpm run sync-remark42-rewrite` | Regenerates the Remark42 rewrite in `vercel.json` from `REMARK42_UPSTREAM_ORIGIN` |
| `pnpm run check-remark42-rewrite` | CI check that `vercel.json`'s Remark42 rewrite matches `REMARK42_UPSTREAM_ORIGIN` |
| `pnpm run analyze` | Runs `astro build` only (no social-image step), then Vercel static-build analysis |
| `pnpm run lighthouse` | Lighthouse HTML report (start dev server first) |
| `pnpm run performance` | Runs `pnpm run build`, then `pnpm run analyze` |
| `pnpm run audit-performance` | Full `pnpm run build`, then Lighthouse performance JSON |
| `pnpm run lighthouse` | Lighthouse HTML report against `dist/`; starts and stops its own preview server |
| `pnpm run audit-performance` | Same, performance category only, JSON output |

CI’s format step **checks**; it does not rewrite or open a follow-up commit. After `pnpm install`, a pre-commit hook runs Prettier on staged files so commits already match that check.

### Previewing and measuring a build locally

`pnpm run preview` serves `dist/client` from `scripts/serve-dist.mjs`. It shows the
prerendered pages and assets; it does **not** serve the on-demand `/api/quotes`
route or apply any `vercel.json` redirect, rewrite, header, or compression rule.
Verify host behavior against a deployment URL, not against this server.

`astro preview` (`pnpm run preview:vercel`) is delegated to the Vercel CLI by
`@astrojs/vercel`. Without that CLI installed it fails with "Preview server
process exited before becoming ready", which is why it is no longer the default
`preview` script.

`pnpm run lighthouse` and `pnpm run audit-performance` start and stop that same
static server themselves, so run `pnpm run build` first and nothing else. Reports
land in `reports/` (gitignored). They measure localhost over plain HTTP — useful
as a before/after baseline, not as field performance.

## Build (social images)

`pnpm run build` runs `scripts/generate-social-images.js` before `astro build`.
Expand Down
51 changes: 29 additions & 22 deletions docs/TECHNICAL-AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

This document is the system map the constitution already points at. It records what the product _is_ technically, where docs/code drift, and which gaps are closed vs deferred.

**Revised 2026-09-10** (audit date above is preserved on purpose): the toolchain row
in §2, the gate table in §7, and the cache note in §10 were corrected against the
`codex/optimization-roadmap` work. Everything else still describes the 2026-07-28
snapshot and has not been re-verified.

---

## 1. Product shape (one sentence)
Expand All @@ -17,17 +22,17 @@ Public field notes (essays, household recipes, book library) on a **hybrid Astro

## 2. Stack (truth)

| Layer | Actual (resolved) | Docs that were wrong |
| --------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------- |
| Framework | **Astro 7.x** (`astro@7.1.4`) | README / constitution / specify-rules said Astro 6 |
| Output | **Hybrid** — default static + `prerender = false` on quotes | README said “static output” |
| Adapter | `@astrojs/vercel@11` | OK |
| UI | Tailwind CSS 4 + Vite plugin; Astro Fonts (DM Sans, Fraunces, Literata, JetBrains Mono) | OK |
| Content | MD/MDX via `@astrojs/mdx`, collection `blog` | OK |
| Language | TypeScript 5.9 | OK |
| Package manager | pnpm (CI: pnpm 10, Node 22.12) | OK |
| Image | Sharp; AVIF sources → social JPEG/PNG | OK |
| Analysis | `sentiment`, `reading-time`, brain-science utils | OK |
| Layer | Actual (resolved) | Docs that were wrong |
| --------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| Framework | **Astro 7.x** (`astro@7.2.2` as of 2026-09-10) | README / constitution / specify-rules said Astro 6 |
| Output | **Hybrid** — default static + `prerender = false` on quotes | README said “static output” |
| Adapter | `@astrojs/vercel@11` | OK |
| UI | Tailwind CSS 4 + Vite plugin; Astro Fonts (DM Sans, Fraunces, Source Serif 4, JetBrains Mono) | OK |
| Content | MD/MDX via `@astrojs/mdx`, collection `blog` | OK |
| Language | TypeScript 5.9 | OK |
| Package manager | pnpm 12.3.4 (`packageManager`), Node 22.12.0 (`.nvmrc`); CI reads both | OK |
| Image | Sharp; AVIF sources → social JPEG/PNG | OK |
| Analysis | `sentiment`, `reading-time`, brain-science utils | OK |

---

Expand Down Expand Up @@ -114,16 +119,18 @@ No accounts. No server-side reading progress. Constitution principle IV applies.

## 7. Quality gates (actual vs claimed)

| Gate | Status |
| ----------------------------------- | ------------------------------------------------------- |
| `pnpm run format:check` | CI |
| `pnpm run check` | CI |
| `pnpm run lint` | CI |
| `pnpm run build` | CI (+ social image step) |
| `pnpm run validate-feeds` | **Wired** — needs `dist/` from a prior build |
| `pnpm run audit-frontmatter` | **Wired** — walks `src/content/p` |
| `pnpm run validate-structured-data` | **Wired** — smoke-checks structured-data module surface |
| Unit / e2e tests | **None** (constitution acknowledges this) |
| Gate | Status |
| ----------------------------------- | -------------------------------------------------------- |
| `pnpm run format:check` | CI |
| `pnpm run check` | CI |
| `pnpm run lint` | CI |
| `pnpm run build` | CI (+ social image step) |
| `pnpm run validate-feeds` | CI, after the build (needs `dist/`) |
| `pnpm run audit-frontmatter` | CI, before the build — walks `src/content/p` |
| `pnpm run validate-structured-data` | CI — smoke-checks structured-data module surface only |
| `pnpm run check-remark42-rewrite` | CI — `vercel.json` rewrite vs `REMARK42_UPSTREAM_ORIGIN` |
| Unit tests (`pnpm test`) | CI — see §9 for coverage |
| Browser / e2e tests | **None** — Playwright is installed but unconfigured |

---

Expand Down Expand Up @@ -168,7 +175,7 @@ Unit tests (`pnpm test`) cover publish filters, SEO routing, feed HTML sanitizat

## 10. Scaling risks (known)

- Writing Insights lexicons live in `src/utils/brainScience/vocabulary.ts` (EN+ES). Flesch / word / sentence metrics + objective metrics + sentiment + posts fetch are memoized for the build process. Meta disk cache is versioned (v2); signature still ignores title and same-length body edits.
- Writing Insights lexicons live in `src/utils/brainScience/vocabulary.ts` (EN+ES). Flesch / word / sentence metrics + objective metrics + sentiment + posts fetch are memoized for the build process. The meta disk cache is versioned (v3) and its signature hashes title + body plus `pubDate`, so same-length body edits and title changes invalidate (corrected 2026-09-10).
- Dual redirect tables invite drift (host rules stay on Vercel; path redirects in Astro).
- Graphify graph may lag HEAD; refresh with `graphify update .` after code changes.

Expand Down
49 changes: 26 additions & 23 deletions docs/performance-optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

## Overview

This document outlines the comprehensive performance optimizations implemented for the Blog to improve First Contentful Paint (FCP) and Largest Contentful Paint (LCP) metrics, following Astro blog best practices.
This document outlines the performance optimizations implemented for the Blog to improve First Contentful Paint (FCP) and Largest Contentful Paint (LCP) metrics, following Astro blog best practices.

> **Accuracy note — 2026-09-10.** Parts of this guide describe intended practice
> rather than verified current behavior, and no field Core Web Vitals data backs
> the targets below. The font, testing-command, and image sections were corrected
> against the code on this date. Treat the remaining sections as guidance to
> re-verify before citing, and measure before claiming an improvement.

## Core Web Vitals Targets

Expand All @@ -15,26 +21,21 @@ This document outlines the comprehensive performance optimizations implemented f

### 1. Font Loading Optimization

#### Critical Font Preloading

- Preload critical fonts (Open Sans 400, 600, Source Serif Pro 600) using `@font-face` with `font-display: swap`
- Implement font preloading in HTML head for above-the-fold content
- Use system font fallbacks to prevent invisible text during font loading
#### Fonts actually in use

```css
@font-face {
font-family: 'Open Sans';
font-weight: 400;
font-display: swap;
src: url('...') format('woff2');
}
```
Fonts are configured through Astro's `fonts` integration in `astro.config.mjs`, not
hand-written `@font-face` rules. The four families are **DM Sans**, **Fraunces**,
**Source Serif 4**, and **JetBrains Mono**. (Earlier revisions of this document
named Open Sans and Source Serif Pro; neither is in the project.)

#### Font Loading Strategy
Astro emits the `@font-face` rules, fallback metrics, and preload hints for the
subsets it decides are needed. Do not add parallel `@font-face` declarations —
they would compete with what the integration generates.

- **Critical fonts**: Loaded immediately with preload
- **Additional weights**: Loaded asynchronously with `display=swap`
- **Fallback fonts**: System fonts used until custom fonts load
A build copies 41 font files into the output. That is the number of files
generated across families, weights, and subsets; it is **not** the number any
single page downloads. Measure real font transfer per route before treating it as
a problem.

### 2. Critical CSS Inlining

Expand Down Expand Up @@ -136,12 +137,14 @@ pnpm run lighthouse
pnpm run audit-performance
```

#### Build Analysis
`pnpm run lighthouse` writes an HTML report and `pnpm run audit-performance`
writes performance-only JSON, both under `reports/` (gitignored). Each starts and
stops its own `astro preview` server, so run `pnpm run build` first — they measure
`dist/` and fail if it is missing.

```bash
pnpm run analyze
pnpm run performance
```
The former `analyze` and `performance` scripts were removed on 2026-09-10: they
shelled out to `@astrojs/vercel/static-build`, which the installed adapter does
not provide, and `performance` ran the full build twice.

### Monitoring in Development

Expand Down
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@
"url": "https://github.com/antoniwan/notes.git"
},
"homepage": "https://notes.antoniwan.online",
"packageManager": "pnpm@12.3.4",
"engines": {
"node": ">=22.12.0",
"pnpm": ">=12"
},
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "node scripts/generate-social-images.js && astro build",
"preview": "astro preview",
"preview": "node scripts/serve-dist.mjs",
"preview:vercel": "astro preview",
"astro": "astro",
"generate-social-images": "node scripts/generate-social-images.js",
"generate-favicons": "node scripts/generate-favicons.js",
"analyze": "astro build && npx @astrojs/vercel/static-build",
"lighthouse": "npx lighthouse http://localhost:4321 --output=html --output-path=./lighthouse-report.html",
"performance": "pnpm run build && pnpm run analyze",
"audit-performance": "pnpm run build && npx lighthouse http://localhost:4321 --only-categories=performance --output=json --output-path=./performance-audit.json",
"lighthouse": "node scripts/lighthouse.mjs",
"audit-performance": "node scripts/lighthouse.mjs --performance-only",
"prepare": "simple-git-hooks",
"format": "prettier --write .",
"format:check": "prettier --check .",
Expand Down
Loading
Loading