Migrate every find from src/content/find/<slug>/ to src/content/find/<year>/<month>/<slug>/ — all 289 existing finds moved into 2026/05/. The find content-collection loader gets a new leafSlug generateId so ids (and therefore URLs at /find/<slug>/) stay flat; src/lib/find-hero.ts switches to a recursive **/hero glob. Bundle items[], promotedTo/fromFind keys, /sources cross-refs, and category/tag pages all keep working unchanged. Skill docs (daily-finds, build-review, build-bundle, cluster-bundles), README, and CHANGELOG updated for the new on-disk path. Also: 67 new finds from the 2026-05-09 capture run (Web Curios 8, MoMA 7, r/macapps 7, r/dadjokes 6, icanhazdadjoke 6, Wallpaper* Travel 5, Snow Peak 5, Kottke 3, Boutique Homes 3, etc.); felo-ergonic-screwdriver-set rolled back per editorial decision.
125 lines
6.1 KiB
Markdown
125 lines
6.1 KiB
Markdown
---
|
||
name: build-bundle
|
||
description: Draft a 'bundle' (themed editorial roundup of finds) for unique.rzen.dev. Selects find items from src/content/find/, groups them under a theme, and writes a new file at src/content/bundles/. Use when the user runs /build-bundle, asks to "draft today's bundle", "build a roundup", "make a bundle", or "make a catch".
|
||
disable-model-invocation: true
|
||
---
|
||
|
||
# build-bundle
|
||
|
||
Author one editorial bundle (collection: `bundles`) that references existing find items. Each bundle has a theme, a short blurb, and an `items` array listing find slugs. The grid layout is rendered by the page template — this skill only writes the MDX.
|
||
|
||
Invoke from inside the `unique.rzen.dev` repo.
|
||
|
||
## Inputs
|
||
|
||
- `src/content/find/<year>/<month>/<slug>/index.mdx` — the available pool of finds (folders are nested by date but slugs are globally unique; recurse with `find src/content/find -name index.mdx` or rely on the `find` content collection's id which is the leaf slug).
|
||
- `src/content/bundles/*.mdx` — existing bundles (used to detect already-bundled finds).
|
||
- Optional argument: a theme phrase (e.g. "single-virtue tools", "things-disguised-as-other-things").
|
||
- Optional argument: an explicit list of find slugs the user wants in the bundle.
|
||
|
||
## Procedure
|
||
|
||
### 1. Determine candidate pool
|
||
|
||
- **If the user provided explicit slugs (seed mode)** — usually the case when the user clicks the "Bundle this selection" button on `/find/`. Treat the slugs as a *seed*, not a final list.
|
||
1. Read each seed find's frontmatter (`tags`, `topics`, `subtitle`, `description`) and body.
|
||
2. Infer the through-line — the smallest phrase that captures what these finds have in common (a virtue, a use, a feeling, a structural pattern). Lean specific. Good: *"single-virtue tools"*, *"things that pour clean"*, *"objects that exist for no reason except someone wanted them to"*, *"vintage-leaning everyday carry"*. Bad: *"useful things"*, *"cool stuff"*.
|
||
3. Compute the **unbundled pool**: every find that does NOT appear in the `items[]` array of any existing `src/content/bundles/*.mdx` bundle. The seed slugs may or may not be in this pool — they're the seed regardless.
|
||
4. From the unbundled pool, find every entry that fits the inferred theme. Use frontmatter signals (tags, topics, subtitle) and body content. Don't reach: three weakly-fitting items is worse than zero. If a candidate's fit feels strained, drop it.
|
||
5. Combine `seed + qualifying unbundled matches`, deduped. Cap at **8 total** (per sizing notes below). If the combined list exceeds 8, drop the weakest fits — never drop a seed slug; if the seed itself is more than 8, ask the user which to drop.
|
||
6. Produce one cluster (theme phrase + final items list). Continue to step 2 to confirm before writing.
|
||
|
||
- **If the user provided a theme phrase only:** scan all finds, surface the 8–12 that best fit the theme.
|
||
|
||
- **If neither:** scan recent (last ~14 days) finds that are not yet referenced by any existing bundle. From the unbundled pool, propose 1–3 thematic clusters of 5–10 finds each, with a candidate theme phrase per cluster.
|
||
|
||
Re-use is allowed — a find can appear in multiple bundles under different themes. But by default, prefer surfacing **unbundled** finds first; only reach for re-uses when the theme genuinely calls for them.
|
||
|
||
### 2. Confirm the cluster
|
||
|
||
Always show the proposed cluster(s) before writing anything. The exact format depends on which path step 1 took.
|
||
|
||
**Seed mode** (slugs given) — split the seed from the expansion so the user can see what the inferred theme pulled in:
|
||
|
||
```
|
||
Theme inferred from seed: "<theme phrase>"
|
||
|
||
Seed (your selection):
|
||
- <slug>: <name>
|
||
- ...
|
||
|
||
Expansion (unbundled finds matching the theme):
|
||
- <slug>: <name>
|
||
- ...
|
||
|
||
Final list (capped at 8): <N> items
|
||
```
|
||
|
||
**Multi-cluster mode** (no input):
|
||
|
||
```
|
||
Cluster A — "<theme phrase>"
|
||
- <slug>: <name>
|
||
- <slug>: <name>
|
||
- ...
|
||
|
||
Cluster B — "<theme phrase>"
|
||
...
|
||
```
|
||
|
||
Wait for the user to confirm, refine the theme phrase, or drop/swap items. Don't write anything until confirmed.
|
||
|
||
### 3. Write the bundle
|
||
|
||
File: `src/content/bundles/<YYYY-MM-DD>-<theme-slug>/index.mdx` (theme-slug = kebab-case of the theme phrase, lowercase, ~30 chars max).
|
||
|
||
Frontmatter:
|
||
|
||
```yaml
|
||
---
|
||
title: "<Theme phrased as a noun phrase, max ~60 chars>"
|
||
date: YYYY-MM-DD
|
||
tags: [daily-finds, <topic-tag-1>, <topic-tag-2>]
|
||
description: "<One-line description for meta tags. Reuse the title if nothing better.>"
|
||
blurb: "<One short editorial sentence; appears as the bundle's secondary line in the home feed.>"
|
||
items:
|
||
- <slug>
|
||
- <slug>
|
||
- ...
|
||
---
|
||
```
|
||
|
||
Body: a brief audience-facing intro paragraph (1–2 sentences max) — open with "Today's catch" or a close variant; name what links these finds; vary the wording day to day. **No** numbered candidates, no headings inside the body, no list items — the grid renders from `items[]`.
|
||
|
||
Example body:
|
||
|
||
```mdx
|
||
> Today's catch from a morning sweep across the daily feeds. Five small things, each one built around exactly one virtue — a single sound, a single diagnostic, a single disguise, a single object made, a single quiet joke.
|
||
```
|
||
|
||
### 4. Verify
|
||
|
||
- Confirm every slug in `items[]` exists at `src/content/find/<year>/<month>/<slug>/index.mdx` (use `find src/content/find -type d -name "<slug>"` to locate). If any is missing, abort and report — never reference a non-existent find.
|
||
- Run `npm run build` to confirm the new bundle compiles.
|
||
|
||
### 5. Update the log
|
||
|
||
Append to `daily-finds.log.md`:
|
||
|
||
```
|
||
## YYYY-MM-DD — bundle: <theme>
|
||
|
||
Slug: <date>-<theme-slug>
|
||
Items referenced:
|
||
- <slug>
|
||
- <slug>
|
||
...
|
||
```
|
||
|
||
## Notes
|
||
|
||
- Default to 5 finds per bundle. Use 3 for tighter themes, up to 8 if the theme really supports it. Beyond 8 the grid stops being scannable.
|
||
- The body is for the audience, not the user. Never write "pick one", "candidates", "today's pick" — those are internal terms.
|
||
- Never edit existing find files when building a bundle. The find body and metadata are stable; the bundle only references them.
|
||
- If a find that's referenced has been graduated to a review (its frontmatter has `promotedTo`), it can still appear in the bundle — the grid card simply links to the find page, which itself links forward to the review.
|