A calm place to write long-form, and publish it to the open social web.
skypress.blog/
1# 0016 — Read routes delegate orchestration to the read-context module
2
3- **Status:** Accepted
4- **Date:** 2026-06-10
5- **Scope:** the public read path (`src/pages/[author]/**`, `src/lib/reader/read-context.ts`)
6
7## Context
8
9Every read route (author index, publication home, article, RSS) hand-assembled the same
10resolution spine in its frontmatter: strip the `@` from the URL param, resolve
11handle → DID → PDS (Decision 0007), match a publication by slug, join documents by
12`value.site === publication.uri` (Decision 0010), and map each miss to an error scene.
13That was ~230 near-duplicate lines across four files, and the join key, the 100-document
14fetch bound, and the blob-ref → logo-URL construction were caller knowledge repeated
153–4×. Because the orchestration lived in `.astro` frontmatter, it could only be "tested"
16by regex pins against the page source (see rule 8 in AGENTS.md for why container
17rendering is out).
18
19## Decision
20
21One deep module, `src/lib/reader/read-context.ts`, owns the spine behind three entry
22points — `resolveAuthorContext`, `resolvePublicationContext`, `resolveArticleContext` —
23each returning `{ ok: true, context }` or `{ ok: false, error: ErrorSceneCopy }`.
24
25- **Pages keep only presentation.** A new read surface (AMP, embeds, …) must call the
26 read-context interface, not re-assemble `resolveAuthor` + slug-match + site-join.
27- The module returns publications with `logoUrl` prebuilt, so callers never touch
28 `BlobRefJson` internals.
29- The per-route independent fetches (profile, publication list, documents/record) run
30 in parallel inside the module; the old pages ran them serially.
31- The article render pipeline (`resolveBlobImageUrls → renderBlocks →
32 sanitizeArticleHtml`) deliberately stays in the article page / feed builder — sealing
33 it behind its own interface is a separate, orthogonal deepening. *(Since done:
34 Decision 0018 sealed it behind `src/lib/reader/render-article.ts`.)*
35- The shallow wrappers this obsoleted (`listReaderPublications`,
36 `resolveReaderPublication`) were deleted; `listAllReaderPublications` remains the one
37 reader-side publication fetch.
38
39## Consequences
40
41- The orchestration is behaviourally unit-tested in `read-context.test.ts` (mocking only
42 the network seam: `identity.ts` + `records.ts`); the colocated `_*.meta.test.ts` source
43 pins shrank to template-wiring guards.
44- On not-found paths the module may fetch slightly more than the old serial code did
45 (the parallel fetches start before the publication match is known) — accepted for the
46 happy-path latency win.
47- RSS 404 bodies are now a uniform `Not found` instead of echoing the handle.