site: rename "finds" superpost collection to "bundles"
Disambiguates the one-letter `find` (item) vs `finds` (roundup) split that
kept causing confusion. Audience-facing now: `/bundles/<slug>/`, "Bundle"
labels in home/footer/search.
Internals: `getCollection('finds')` → `('bundles')`, `Entry.type 'finds'`
→ `'bundle'`, `OgFallback 'finds'` → `'bundles'`, `findListMosaic` →
`bundleMosaic`, `FindsPost` → `Bundle`, `findsReferencing` →
`bundlesReferencing`. Build script + generated mosaic dir follow
(`scripts/build-bundle-mosaics.mjs`, `src/generated/bundle-mosaics/`).
Skills `build-finds` → `build-bundle`, `cluster-finds` →
`cluster-bundles`. The `find` collection (individual items) is unchanged.
No URL redirects: low external-link surface area.
This commit is contained in:
@@ -1,39 +1,39 @@
|
||||
---
|
||||
name: build-finds
|
||||
description: Draft a 'finds' superpost (editorial roundup) for unique.rzen.dev. Selects find items from src/content/find/, groups them under a theme, and writes a new file at src/content/finds/. Use when the user runs /build-finds, asks to "draft today's finds post", "build a roundup", or "make a catch".
|
||||
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-finds
|
||||
# build-bundle
|
||||
|
||||
Author one editorial superpost (collection: `finds`) that references existing find items. Each superpost 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.
|
||||
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/*.mdx` — the available pool of finds.
|
||||
- `src/content/finds/*.mdx` — existing superposts (used to detect already-referenced finds).
|
||||
- `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 post.
|
||||
- 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 "Make a finds collection" button on `/find/`. Treat the slugs as a *seed*, not a final list.
|
||||
- **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 **unreferenced pool**: every find that does NOT appear in the `items[]` array of any existing `src/content/finds/*.mdx` superpost. The seed slugs may or may not be in this pool — they're the seed regardless.
|
||||
4. From the unreferenced 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 unreferenced 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.
|
||||
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 superpost. From the unreferenced pool, propose 1–3 thematic clusters of 5–10 finds each, with a candidate theme phrase per cluster.
|
||||
- **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 superposts under different themes. But by default, prefer surfacing **unreferenced** finds first; only reach for re-uses when the theme genuinely calls for them.
|
||||
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
|
||||
|
||||
@@ -48,7 +48,7 @@ Seed (your selection):
|
||||
- <slug>: <name>
|
||||
- ...
|
||||
|
||||
Expansion (unreferenced finds matching the theme):
|
||||
Expansion (unbundled finds matching the theme):
|
||||
- <slug>: <name>
|
||||
- ...
|
||||
|
||||
@@ -69,9 +69,9 @@ 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 superpost
|
||||
### 3. Write the bundle
|
||||
|
||||
File: `src/content/finds/<YYYY-MM-DD>-<theme-slug>.mdx` (theme-slug = kebab-case of the theme phrase, lowercase, ~30 chars max).
|
||||
File: `src/content/bundles/<YYYY-MM-DD>-<theme-slug>/index.mdx` (theme-slug = kebab-case of the theme phrase, lowercase, ~30 chars max).
|
||||
|
||||
Frontmatter:
|
||||
|
||||
@@ -81,7 +81,7 @@ 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 post's secondary line in the home feed.>"
|
||||
blurb: "<One short editorial sentence; appears as the bundle's secondary line in the home feed.>"
|
||||
items:
|
||||
- <slug>
|
||||
- <slug>
|
||||
@@ -99,15 +99,15 @@ Example body:
|
||||
|
||||
### 4. Verify
|
||||
|
||||
- Confirm every slug in `items[]` exists at `src/content/find/<slug>.mdx`. If any is missing, abort and report — never reference a non-existent find.
|
||||
- Run `npm run build` to confirm the new superpost compiles.
|
||||
- Confirm every slug in `items[]` exists at `src/content/find/<slug>/index.mdx`. 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 — superpost: <theme>
|
||||
## YYYY-MM-DD — bundle: <theme>
|
||||
|
||||
Slug: <date>-<theme-slug>
|
||||
Items referenced:
|
||||
@@ -118,7 +118,7 @@ Items referenced:
|
||||
|
||||
## Notes
|
||||
|
||||
- Default to 5 finds per superpost. Use 3 for tighter themes, up to 8 if the theme really supports it. Beyond 8 the grid stops being scannable.
|
||||
- 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 superpost. The find body and metadata are stable; the superpost 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 superpost — the grid card simply links to the find page, which itself links forward to the review.
|
||||
- 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.
|
||||
@@ -72,14 +72,34 @@ Edit `src/content/find/<slug>.mdx` frontmatter: add `promotedTo: <review-slug>`.
|
||||
|
||||
(Slug usually matches the find's slug, so `promotedTo` and the find's filename match.)
|
||||
|
||||
### 6. Verify
|
||||
### 6. Register the hero in `src/lib/hero.ts` (REQUIRED if a hero asset exists)
|
||||
|
||||
This is the step that has bitten us repeatedly. **The home grid and category cards do NOT auto-discover review hero assets** — they only show a hero if the review's slug is a key in the `heroes` record exported by `src/lib/hero.ts`. Skipping this step ships a review whose grid card is empty.
|
||||
|
||||
For a review with a static image hero (`hero.png`, `hero.jpg`, `hero.webp`):
|
||||
|
||||
1. Add an `import` line near the top, alphabetically grouped with the other image imports. The variable name is the slug in camelCase. Example for slug `tablepro`:
|
||||
```ts
|
||||
import tablepro from '../content/reviews/tablepro/hero.png';
|
||||
```
|
||||
2. Add an entry to the `heroes` record, alphabetically by key:
|
||||
```ts
|
||||
tablepro: { kind: 'image', src: tablepro },
|
||||
```
|
||||
|
||||
For a review with a video hero (`.mp4`), put the import in the second (video) import group and the record entry uses `kind: 'video'`. Match the existing pattern.
|
||||
|
||||
If the review legitimately has no hero asset (rare — e.g. text-only reviews like `snewpapers-newspaper-archive`, `sindre-sorhus-older-mac-apps`), explicitly note it in the log entry from step 8 ("no hero asset — text-only review") so the omission is intentional and traceable.
|
||||
|
||||
### 7. Verify
|
||||
|
||||
- Run `npm run build`. Confirm:
|
||||
- `/reviews/<slug>/` exists, shows the source badge and "← First surfaced…" backlink to the find page.
|
||||
- `/find/<slug>/` shows the "→ Read the full review" callout pointing to the review.
|
||||
- Any superpost that references the find still renders fine; the find card on the grid remains.
|
||||
- Any bundle that references the find still renders fine; the find card on the grid remains.
|
||||
- Visit `/grid/` (or `/`) and confirm the new review's tile renders with its hero — not blank. **If the tile is empty, step 6 was skipped.**
|
||||
|
||||
### 7. Update the log
|
||||
### 8. Update the log
|
||||
|
||||
Append to `daily-finds.log.md`:
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
---
|
||||
name: cluster-finds
|
||||
description: Batch-cluster every existing find item in src/content/find/ into 4-7 thematic 'finds' superposts. Use when the user wants to bring the find pool up to date all at once — ("re-cluster the finds", "build superposts for everything", "spread the new finds across themed posts"). For drafting one superpost from a single theme, use build-finds instead.
|
||||
name: cluster-bundles
|
||||
description: Batch-cluster every existing find item in src/content/find/ into 4-7 thematic bundles. Use when the user wants to bring the find pool up to date all at once — ("re-cluster the finds", "build bundles for everything", "spread the new finds across themed bundles"). For drafting one bundle from a single theme, use build-bundle instead.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# cluster-finds
|
||||
# cluster-bundles
|
||||
|
||||
Bulk version of `build-finds`. Reads the entire `src/content/find/` pool, partitions it into themed clusters, and writes one `src/content/finds/<date>-<slug>.mdx` superpost per cluster. The audience-facing site treats `finds` as the primary discovery vehicle for individual `find` items, so the goal is *coverage*: by the end of the run, almost every find that deserves a home should have one.
|
||||
Bulk version of `build-bundle`. Reads the entire `src/content/find/` pool, partitions it into themed clusters, and writes one `src/content/bundles/<date>-<slug>/index.mdx` bundle per cluster. The audience-facing site treats `bundles` as the primary discovery vehicle for individual `find` items, so the goal is *coverage*: by the end of the run, almost every find that deserves a home should have one.
|
||||
|
||||
Invoke from inside the `unique.rzen.dev` repo.
|
||||
|
||||
## Inputs
|
||||
|
||||
- `src/content/find/*.mdx` — the full pool to cluster.
|
||||
- `src/content/finds/*.mdx` — already-written superposts. Their `items[]` lists are the prior clustering — read these to know which finds already have a home and what themes have been used.
|
||||
- `src/content/bundles/*.mdx` — already-written bundles. Their `items[]` lists are the prior clustering — read these to know which finds already have a home and what themes have been used.
|
||||
- Optional argument: a number of clusters to target (default: model's call, usually 4-7).
|
||||
- Optional argument: a list of theme phrases to seed clustering around.
|
||||
|
||||
@@ -23,7 +23,7 @@ Invoke from inside the `unique.rzen.dev` repo.
|
||||
|
||||
Read the frontmatter and body of every find. Note the `name`, `subtitle`, `topics`, `tags`, and the body's stated *virtue* (the specific thing that makes the item delightful). Tags and topics are hints, not the answer — the body usually tells you what cluster a find actually belongs to.
|
||||
|
||||
Also read every existing finds file's `items[]` array — these are pre-clustered and should not be re-clustered into a new redundant theme. They can still be *re-used* in a new cluster if the new theme genuinely calls for them (see overlap rules).
|
||||
Also read every existing bundle's `items[]` array — these are pre-clustered and should not be re-clustered into a new redundant theme. They can still be *re-used* in a new cluster if the new theme genuinely calls for them (see overlap rules).
|
||||
|
||||
### 2. Partition into clusters
|
||||
|
||||
@@ -31,13 +31,13 @@ Aim for 4-7 clusters. Each cluster should:
|
||||
|
||||
- Hang off **one specific editorial angle**, phrasable in 4-8 words ("Pocket carry", "The morning kitchen ritual", "Travel as a kind of attention"). Avoid generic buckets ("Tools", "Apps") — those are tag pages.
|
||||
- Hold **3-8 items**. Below 3 the grid feels thin; above 8 it stops being scannable.
|
||||
- Be **distinct from existing finds posts** in angle, even if a couple of items repeat.
|
||||
- Be **distinct from existing bundles** in angle, even if a couple of items repeat.
|
||||
|
||||
Coverage check: after partitioning, every find should appear in at least one cluster (existing or new). If a find genuinely fits no theme, leave it uncovered and report it — don't pad a cluster with a misfit.
|
||||
|
||||
### 3. Overlap rules
|
||||
|
||||
Re-use is allowed but rationed. Set a soft budget: at most ~20% of items across all new posts may be repeats from existing posts or from other new posts in this same run. Concretely, for a 25-item pool, that's ~5 reuses total. Track repeats as you draft.
|
||||
Re-use is allowed but rationed. Set a soft budget: at most ~20% of items across all new bundles may be repeats from existing bundles or from other new bundles in this same run. Concretely, for a 25-item pool, that's ~5 reuses total. Track repeats as you draft.
|
||||
|
||||
When choosing which items to repeat: only repeat a find when the second theme genuinely illuminates a different facet of it. "It also kind of fits over here" is not enough.
|
||||
|
||||
@@ -52,15 +52,15 @@ Cluster A — "<theme>" (N items)
|
||||
Cluster B — "<theme>" (N items)
|
||||
...
|
||||
|
||||
Overlap with existing posts: <slug> appears in <existing-post> + <new cluster>; ...
|
||||
Overlap with existing bundles: <slug> appears in <existing-bundle> + <new cluster>; ...
|
||||
Uncovered finds: <list, or "none">
|
||||
```
|
||||
|
||||
Wait for the user to confirm, refine, swap, or merge clusters. Do not write files until they sign off.
|
||||
|
||||
### 5. Write each superpost
|
||||
### 5. Write each bundle
|
||||
|
||||
Same file format as `build-finds`. For each cluster, write `src/content/finds/<YYYY-MM-DD>-<theme-slug>.mdx`:
|
||||
Same file format as `build-bundle`. For each cluster, write `src/content/bundles/<YYYY-MM-DD>-<theme-slug>/index.mdx`:
|
||||
|
||||
```yaml
|
||||
---
|
||||
@@ -75,19 +75,19 @@ items:
|
||||
- ...
|
||||
---
|
||||
|
||||
> <One audience-facing sentence or two that names what links these finds. Vary the opening — don't reuse 'Today's catch' for every post in the run. Read like a magazine standfirst, not a checklist.>
|
||||
> <One audience-facing sentence or two that names what links these finds. Vary the opening — don't reuse 'Today's catch' for every bundle in the run. Read like a magazine standfirst, not a checklist.>
|
||||
```
|
||||
|
||||
Body rules (same as `build-finds`):
|
||||
Body rules (same as `build-bundle`):
|
||||
|
||||
- 1-2 sentence intro paragraph in a blockquote. No headings, no list items, no numbered candidates — the grid handles the items.
|
||||
- Audience-facing language. Never write "today's pick", "candidates", "winners".
|
||||
- Vary the opening across the batch. With 4-7 posts going out the same day, repeating "Today's catch" five times feels mechanical.
|
||||
- Vary the opening across the batch. With 4-7 bundles going out the same day, repeating "Today's catch" five times feels mechanical.
|
||||
|
||||
### 6. Verify
|
||||
|
||||
- Every slug in every `items[]` must exist at `src/content/find/<slug>.mdx`. Confirm before declaring done.
|
||||
- Run `npm run build`. If any superpost fails to compile, fix and re-run.
|
||||
- Every slug in every `items[]` must exist at `src/content/find/<slug>/index.mdx`. Confirm before declaring done.
|
||||
- Run `npm run build`. If any bundle fails to compile, fix and re-run.
|
||||
|
||||
### 7. Update the log
|
||||
|
||||
@@ -96,11 +96,11 @@ Append to `daily-finds.log.md`:
|
||||
```
|
||||
## YYYY-MM-DD — cluster run
|
||||
|
||||
Wrote <N> superposts from a pool of <M> finds.
|
||||
Reuses across posts: <count> (budget: ~20%).
|
||||
Wrote <N> bundles from a pool of <M> finds.
|
||||
Reuses across bundles: <count> (budget: ~20%).
|
||||
Uncovered finds: <list, or "none">.
|
||||
|
||||
Posts created:
|
||||
Bundles created:
|
||||
- <date>-<slug-A>: <title> (N items)
|
||||
- <date>-<slug-B>: <title> (N items)
|
||||
- ...
|
||||
@@ -108,7 +108,7 @@ Posts created:
|
||||
|
||||
## Notes
|
||||
|
||||
- This skill complements, doesn't replace, `build-finds`. After a cluster run, `build-finds` is still the right tool for ad-hoc, single-theme posts on top of new captures.
|
||||
- This skill complements, doesn't replace, `build-bundle`. After a cluster run, `build-bundle` is still the right tool for ad-hoc, single-theme bundles on top of new captures.
|
||||
- If two themes you're considering have heavy overlap (≥50% same items), they're really one theme — merge them and pick the stronger framing.
|
||||
- Date all posts with the run date, not staggered — these are siblings written together, and the home feed will sort them naturally by `title`.
|
||||
- Don't edit existing finds posts when running this. If an existing post overlaps with a new cluster you're proposing, either keep the new cluster (overlap-tolerated) or drop it from the plan; do not retroactively rewrite history.
|
||||
- Date all bundles with the run date, not staggered — these are siblings written together, and the home feed will sort them naturally by `title`.
|
||||
- Don't edit existing bundles when running this. If an existing bundle overlaps with a new cluster you're proposing, either keep the new cluster (overlap-tolerated) or drop it from the plan; do not retroactively rewrite history.
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
name: daily-finds
|
||||
description: Capture-only daily sweep for unique.rzen.dev. Scans every source in sources.json, writes one find file per qualifying item to src/content/find/, and nominates any new sources cited in the items it reads (plus optionally via WebSearch). Does NOT pick a winner, write a superpost, or address the user/audience. Picking happens via /build-finds and /build-review. Use when the user asks for "today's finds", "morning sweep", "capture", or runs /daily-finds.
|
||||
description: Capture-only daily sweep for unique.rzen.dev. Scans every source in sources.json, writes one find file per qualifying item to src/content/find/, and nominates any new sources cited in the items it reads (plus optionally via WebSearch). Does NOT pick a winner, write a bundle, or address the user/audience. Picking happens via /build-bundle and /build-review. Use when the user asks for "today's finds", "morning sweep", "capture", or runs /daily-finds.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# daily-finds
|
||||
|
||||
Pure capture: every item that passes the bar becomes a `find` file. No daily cap, no theme detection, no audience-facing post. Whether any of these finds appears in a `finds` superpost or graduates to a review is a separate editorial decision.
|
||||
Pure capture: every item that passes the bar becomes a `find` file. No daily cap, no theme detection, no audience-facing post. Whether any of these finds appears in a `bundle` or graduates to a review is a separate editorial decision.
|
||||
|
||||
Invoke from inside the `unique.rzen.dev` repo.
|
||||
|
||||
@@ -108,7 +108,7 @@ Short summary in chat:
|
||||
```
|
||||
Captured <N> finds across <M> productive sources today.
|
||||
Blocked: <K> sources. New source nominees: <count>.
|
||||
Run /build-finds to draft a superpost from the recent pool, or /build-review <slug> to graduate one to a review.
|
||||
Run /build-bundle to draft a bundle from the recent pool, or /build-review <slug> to graduate one to a review.
|
||||
```
|
||||
|
||||
That's it. No theme, no candidates list, no "pick a winner."
|
||||
|
||||
Reference in New Issue
Block a user