Lossless Changelog
fleet

Pagefind lands on the ai-labs splash — search across the four-repo rollup

The skill says every splash ships search-by-default and I had quietly demoted it to a follow-up. Caught. Pagefind is now wired: header popover with `/` shortcut, full `/search/` page, three filter facets (kind / from / tag), and the UI tokenized to the splash's three-mode contract. 38 detail pages indexed across ai-labs and its three children.

Michael StatonMusic Editoraugmented with Claude Code on Claude Opus 4.7 (1M context)

Why Care?

The maintain-splash-pages skill is explicit: every splash ships search-by-default. It cites Pagefind specifically — confirmed twice already in memopop-site and lfm/splash — and lists it as a v1 acceptance criterion, not a follow-up. When the ai-labs splash went live earlier tonight, I'd quietly relegated Pagefind to a "follow-up" in the changelog entry. The user caught that and called it out: the skill says it's part of v1. Fair. This entry closes the gap.

What landed: a header search box that opens a popover, a full /search/ page with autofocus and a literate intro, a global / keyboard shortcut, and data-pagefind-body markers on every changelog and context-v detail page. The Pagefind UI's CSS variables map to the splash's semantic tokens so search inherits the dark/light/vibrant mode pivot. Three filter facets (kind:, from:, tag:) let a reader scope by document type, source repo, or topic.

For a pseudomonorepo splash that rolls up four repos (ai-labs itself plus context-vigilance-kit, memopop-ai, dididecks-ai), the from: facet is the load-bearing one — a reader interested in just the MemoPop work can filter to it without needing to remember which entries came from where.

What's New?

  • SearchBox.astro at splash/src/components/. Two variants: compact (a <details> popover triggered from the header) and full (a permanent panel used on /search/). Both mount the Pagefind UI bundle. Lifted from lfm/splash with one substantive adaptation — see below.

  • /search/ page at splash/src/pages/search.astro. Full SearchBox with autofocus, a short literate intro, a hint about the dev-vs-build index discrepancy.

  • Pagefind integration in splash/astro.config.mjs — pagefind() runs after astro build and copies the static index into dist/pagefind/.

  • build.format: 'directory' was already set (Pagefind needs stable per-page URLs).

  • data-pagefind-body + filter spans on changelog/[...slug].astro and context-v/[...slug].astro. Each detail page now carries hidden filter spans for kind, from, and any tag value. The entry-head__meta row + back-link + tag chips are wrapped in data-pagefind-ignore so the indexed body is the article content, not the chrome.

  • data-pagefind-ignore="all" on the <ul> lists in changelog/index.astro and context-v/index.astro so list teasers don't compete with detail-page entries for the same query.

  • Header gets <SearchBox compact /> in the actions area + a Search nav link. Mounts to the right of the brand, opens a 380px popover, dismisses on outside-click, focusable via / from anywhere.

  • Pagefind UI tokenized via CSS variables — --pagefind-ui-primary → --color-accent, etc. — so the search UI pivots through the splash's three modes (dark/light/vibrant) with the rest of the page.

  • Deps: astro-pagefind@^1.8.6 + pagefind@^1.5.2 added to splash/package.json, lockfile updated.

The one substantive adaptation from the lfm/splash crib

lfm/splash is a single-project splash (one repo, no submodules). Its filter facets are kind:Changelog | kind:Context + tag:<value>. That works because every entry is from the same source.

ai-labs is a pseudomonorepo splash. Every changelog and context-v entry carries provenance — from (which peer) and from_path (where in that peer's tree) — stamped at rollup time. The skill says explicitly: pseudomonorepos add a third facet: from:<peer>. So the detail pages here emit a <span data-pagefind-filter="from:${from}" hidden> alongside the kind: and tag: spans. A reader interested in only the context-vigilance-kit corner can filter to from:context-vigilance-kit and search just that slice.

The from value is the same provenance value already shown as the pill at the top of every detail card (rendered visibly via <span class="pill pill--accent">{from}</span>), so the filter taxonomy matches what a reader can see on the page.

Verification

Clean build:

$ rm -rf dist && pnpm build
... 42 page(s) built in 2.25s
[pagefind] Pagefind indexed 42 pages
[pagefind] Pagefind wrote index to dist/pagefind

$ cat dist/pagefind/pagefind-entry.json | head -5
{"version":"1.5.2","languages":{"en":{"hash":"en_3dbbe946ce","wasm":"en","page_count":38}}}

The 42 / 38 split is the right shape: 42 routes total in the build, 38 have data-pagefind-body (every changelog detail page + every context-v detail page). The four non-indexed routes are the home, /search/, /changelog/ index, and /context-v/ index — none should appear in search results because their content is the same content the detail pages already index.

$ grep -c "data-pagefind-body" dist/changelog/2026-05-11_02/index.html
1
$ grep -c "data-pagefind-ignore" dist/changelog/index.html
1

After deploy: confirm the search box renders in the header, opens on click and on / keypress, returns results when you type "Pagefind" or "splash" or "context", and that kind:, from:, and tag: filters appear once a query has results.

What's still incomplete versus the skill

Two gaps remain after this entry:

  1. /llms.txt + /llms-full.txt endpoints. The children both serve these now (context-vigilance-kit since 2026-05-09, memopop-ai since 2026-05-11). The mechanical port for the ai-labs splash takes about an hour — copy the two endpoint files from CVK, copy the src/llms/*.md source-of-truth templates, adapt for two collections vs. one, register a CHANGELOG_INDEX + CONTEXTV_INDEX token map. The sitemap filter in astro.config.mjs is already forward-looking — it excludes /llms.txt and /llms-full.txt from sitemap generation, so the endpoint can land without further config changes.

  2. SortControls on /changelog/ and /context-v/ list pages. The skill specifies server-pre-sorted default (date_modified desc), per-page localStorage persistence, and a JS-driven re-order on <li> data-sort-* attributes. Lift verbatim from lfm/splash/src/components/SortControls.astro when prioritized.

Neither is a deploy blocker.

What I learned

When the skill says "v1" about a particular component, treat it as v1 — don't quietly demote it to a follow-up just because the rest of the surface already works. The skill exists precisely so future-me doesn't have to rediscover that Pagefind is the search engine of choice, that the data-pagefind-body silent-opt-out gotcha exists, and that the Pagefind UI's CSS variables are the integration seam for theming. The lift from lfm/splash took longer to justify deferring than it would have to just do in the first place.

The user catching it as quickly as they did — within an hour of the "live!!!!!" celebration — is the right velocity for this kind of correction. Skill-driven conventions are a backstop, not optional polish.

  • The skill: lossless-skills/skills/maintain-splash-pages.md. Specifically the Search — Pagefind by default section and the acceptance checklist's Search returns results when typing into the header popover or /search page line.

  • The exploration that named Pagefind: astro-knots/context-v/explorations/Implementing-Full-Text-Search-by-Default.md.

  • Reference impls cribbed from: lfm/splash/src/components/SearchBox.astro (latest), ai-labs/memopop-ai/apps/memopop-site/src/components/SearchBox.astro (first instance), ai-labs/context-vigilance-kit/splash/src/components/SearchBox.astro (sibling).

  • Yesterday's Pages deploy entry: 2026-05-11_02.md — which incorrectly listed Pagefind as a follow-up. This entry is the correction.