Book-Addition Chronofile Posts — Design (IMP-1)
Date: 2026-07-07
Status: Approved for planning
Source: work/IMPROVEMENTS.md IMP-1 — "Add posts for book additions to library collection, so new items are visible as updates when they are added."
App: apps/reader/ (Astro 5 + pnpm). Builds on the Chronofile feature (Astro content collection at src/content/chronofile/, native glob loader, /chronofile list + post routes, one hand-written inaugural post).
1. Goal & success criteria
Generate one Chronofile post per library book, so the Chronofile doubles as a running accession log — and re-running the generator surfaces newly-added books as new posts.
Done when:
- Every library book (~95, the catalog set) has a generated Chronofile post with a meaningful "added" date and a working link.
- Post dates come from the accession record when one matches, else the git first-add date of the book's files.
- Readable books link to
/read/<slug>; download-only books link to/library. - Generated posts are segregated and idempotent — regenerating never touches hand-written posts (the inaugural).
/chronofilelists all posts interleaved by date; a book post page renders.
2. Data sources
- Books (the catalog set, ~95): repo-root
<dir>/<dir>.epub(the catalog auto-discovery) plus the nestedbooks.config.tsentries (e.g. the WDSD sub-pages). Each book yields{ slug, title, author, epubDir, readable }wherereadable = slug ∈ books.configandepubDiris the book's real repo dir (from its epub path — needed because WDSD slugs differ from their dir). - Accession records:
.wiki/inventory/items/*.md, filename<title-slug>--<YYYY-MM-DD>--<accession-id>.md(the date is the curatorial accession date; the title is in frontmatter too). Excludes_index.md. - Git add date:
git log --diff-filter=A --format=%ad --date=short -- <epubDir>→ the earliest (last line) is the first-add date.
3. Architecture — a prebuild generator (mirrors SP3/SP5)
apps/reader/scripts/build-book-chronofile.mjs:
- Discover the book set (§2) →
Book[]. - Build the accession index: map each
.wiki/inventory/items/*.mdto{ key: normalize(titleSlug), date }.normalize= lowercase,[_ ]→-, strip non-[a-z0-9-], collapse dashes, drop a trailing subtitle after the first---style separator already handled by the filename split. - Resolve each book's date:
accessionIndex.get(normalize(slug)) ?? accessionIndex.get(normalize(title)) ?? gitAddDate(epubDir). Every book resolves to one ISO date. - Resolve each book's link:
readable ? '/read/<slug>' : '/library'. - Emit one post per book to
apps/reader/src/content/chronofile/books/<date>-<slug>.md(§4). Before emitting, delete the existingbooks/directory contents so the set exactly reflects the current library (idempotent; removes posts for any book that left the catalog). - Add a
chronofile-booksnpm script; the generator runs beforepnpm build(likeshards/section-map).
Segregation: hand-written posts live at src/content/chronofile/*.md; generated posts under src/content/chronofile/books/. The generator only ever touches books/. The inaugural is never rewritten.
4. Post format & routing
Each generated books/<date>-<slug>.md:
---
title: "<Book Title>"
date: <resolved ISO date>
summary: "Joined the library — <author>."
tags: [library, book]
---
**<Book Title>**<, by <author>,> is now in the Buckyverse library.
[Read it →](/read/<slug>) ← readable
[In the library →](/library) ← download-only (whichever applies)
(author and the link line are chosen per book; the summary omits the author when unknown.)
Routing: posts in books/ give a content id with a slash (books/<date>-<slug>). Rename the post route src/pages/chronofile/[slug].astro → [...slug].astro (rest param) so both the nested book posts and the flat hand-written posts (2026-07-06-set-free-as-pattern) route correctly. Update its getStaticPaths to params: { slug: post.id } (unchanged) — Astro maps a slashed id onto the rest param. The list page (/chronofile/index.astro) is unaffected (it already links /chronofile/${p.id}).
List polish: render p.data.tags as small chips on /chronofile list items so book-addition entries read as a log and hand-written posts stand out. Low-risk, additive.
5. Volume
This backfills ~95 posts (the Chronofile becomes 1 inaugural + ~95 book-addition entries, interleaved by date). That is the intended accession log. The library tag distinguishes them, enabling later filtering if desired (out of scope now — YAGNI).
6. Scope
In: the generator + npm script; the ~95 generated book posts; the [...slug] route rename; the list tag-chips; unit tests for the pure pieces (normalization/matching, date resolution, post builder); build/deploy verification.
Out: a tag filter UI; RSS; author/cover enrichment beyond what the catalog already exposes; changing the hand-written inaugural; any change to how books are added (this only reflects the current library).
7. Testing
- Unit (
scripts/lib/*.test.mjs, Vitest):normalizeKey:operating_manual_for_spaceship_earthandoperating-manual-for-spaceship-earthproduce the same key.resolveDate: an accession match returns its date; no match falls back to a provided git-date function's value.bookPostMarkdown: readable book → body contains/read/<slug>; download-only →/library; frontmatter has title/date/tags.
- Build smoke:
pnpm chronofile-bookswrites ~95 files undersrc/content/chronofile/books/;pnpm buildsucceeds;/chronofileHTML lists a known book post and the inaugural; a book post page (dist/chronofile/books/<…>/index.html) renders with the resolved date and the correct link; re-running the generator leaves the inaugural file byte-identical. - Live: after deploy,
/chronofile/shows the log and a book post resolves (200).
8. Definition of done
/chronofile is a full library accession log — one dated post per book (accession date when known, git-add otherwise), readable/download links correct, generated posts idempotently segregated from hand-written ones, the [...slug] route serving both, deployed to future.buckyverse.org. Squash-merged to main per the pluck workflow.