--- slug: build-goals-registry title: Build /goals registry + site surface (goal #1) date: 2026-05-25 branch: goals-registry-build pr_number: 45 merge_sha: e923da8 status: shipped related_posts: [sama-v2-goal-chain-gap] --- Goal: Persist /goal slash commands into git as first-class artifacts, mirroring the /blog and /sama patterns. New top-level directory `goals/` holds each goal as a markdown file with YAML frontmatter; the registry lives at src/a31_goals.ts; the site renders /goals (index) and /goals/ (detail) pages; the sitemap automatically picks them up; the workflow change so /goal text always lands as a committed file follows in goal #2. Solves the "we lose /goals" problem at the storage layer first — once /goals/ is the source of truth, future authoring just writes to it. Done when: - New top-level `goals/` directory exists; first migrated goal lives at goals/git-url-drop-owner.md (a placeholder file with the /goal text from PR #42 is fine — goal #2 will do the proper migration). - Frontmatter format defined and parsed: --- slug: title: date: branch: pr_number: merge_sha: status: pending|shipped|abandoned related_posts: [, ...] --- - Layer 0 registry at src/a31_goals.ts: export interface GoalEntry { slug, title, date, branch, prNumber, mergeSha, status, relatedPosts } export const ALL_GOALS: GoalEntry[] = [...] Same shape as ALL_POSTS — drives /goals, /goals/:slug, and the sitemap. - Layer 1 helper src/b32_goals_meta.ts: pure functions parseGoalFrontmatter(body: string) → { meta, body } (no I/O) findGoalByMergeSha(sha: string, all: ReadonlyArray) → GoalEntry | null Sibling test src/b32_goals_meta.test.ts covers: full frontmatter, missing optional fields, malformed frontmatter (returns null), SHA lookup hit/miss, short-vs-full SHA prefix matching (so /GIT/tdd.md/commit/968890f finds a goal whose merge_sha is 968890f8a3bc...). - Layer 3 handlers in src/d21_handlers_goals.ts: goalsLandingHandler → /goals index (table: date · title · status · PR · commit) goalSlugHandler → /goals/:slug detail (rendered markdown, frontmatter badges, links to PR + commit + related posts) - /goals routes registered in src/d21_app.ts. - /goals link added to the main nav in src/b51_render_layout.ts:47 (between /sama and /blog). - Sitemap automatically lists every ALL_GOALS entry via the existing b32_sitemap helper — extend src/d21_app.ts "/sitemap.xml" handler to map ALL_GOALS → /goals/; add "/goals" to STATIC_PATHS in src/b32_sitemap.ts. - /goals/ pages reference their merge commit via the new /GIT/tdd.md/commit/ link AND back-reference any related blog posts in related_posts[]. - All 388+ tests still pass; new helper test adds 5-7 cases. - /sama/v2/verify still reports 7/7 ✓ (anti-fudge). - Deployed; live-verify: * curl https://tdd.md/goals → 200 with HTML listing * curl https://tdd.md/goals/git-url-drop-owner → 200 with the /goal body rendered * curl https://tdd.md/sitemap.xml | grep -c /goals/ → at least 1 * Goals index appears in main nav on every page Constraints (anti-fudge): - One markdown file per goal — no JSON, no multi-goal files, no goal-text embedded inside another file. - Filename is the slug (e.g. git-url-drop-owner.md), NOT the merge SHA. SHA lookup works via the frontmatter field. Rationale: readable URLs, no chicken-and-egg, /goals/ is the canonical permalink. Documented in the file header of a31_goals.ts. - ALL_GOALS is the single source of truth — no second list of goals in handlers or rendered HTML. - Site language English-only. - GitHub flow via flatpak-spawn. - Do NOT change any §4 verifier logic. - Frontmatter parser stays in Layer 1 (pure string-in, struct-out) — no fs.readFile, no path joining. Load-bearing files to read FIRST: - src/a31_blog.ts (the registry pattern that ALL_GOALS should mirror) - src/a31_sama.ts (same — second example of the pattern) - src/d21_handlers_sama.ts (samaLandingHandler + samaSlugHandler — the index + detail pattern) - src/d21_app.ts (route table — where /goals routes get registered) - src/b32_sitemap.ts (extend STATIC_PATHS + add goals URLs to the handler's url list in d21_app.ts) - src/b51_render_layout.ts:47 (nav strip — where /goals link goes) - content/blog/sama-v2-sitemap-implementation-plan.md (the sitemap impl plan post is the closest existing pattern for this kind of registry-driven feature)