the-grid-cc 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.grid/plans/blog-PLAN-SUMMARY.md +518 -0
- package/.grid/plans/blog-block-03.md +253 -0
- package/.grid/plans/blog-block-04.md +287 -0
- package/.grid/plans/blog-block-05.md +235 -0
- package/.grid/plans/blog-block-06.md +325 -0
- package/DEMO_SCRIPT.md +162 -0
- package/HN_POST.md +104 -0
- package/README.md +157 -112
- package/agents/grid-e2e-exerciser.md +311 -0
- package/agents/grid-persona-simulator.md +346 -0
- package/agents/grid-refinement-synth.md +284 -0
- package/agents/grid-visual-inspector.md +229 -0
- package/commands/grid/VERSION +1 -1
- package/commands/grid/help.md +22 -3
- package/commands/grid/mc.md +208 -43
- package/commands/grid/refine.md +283 -0
- package/package.json +1 -1
- package/test-cli/converter.py +206 -0
- package/test-cli/test_data.json +39 -0
- package/test-cli/test_data.yaml +35 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
cluster: james-weatherhead-blog
|
|
3
|
+
block: 03
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 2
|
|
6
|
+
depends_on: [01]
|
|
7
|
+
files_modified: [src/content/config.ts, src/content/blog/sample-post.md, src/pages/blog/[...slug].astro, src/components/PostList.astro, src/components/Tag.astro]
|
|
8
|
+
autonomous: false
|
|
9
|
+
|
|
10
|
+
must_haves:
|
|
11
|
+
truths:
|
|
12
|
+
- "Blog posts are defined as content collection with schema"
|
|
13
|
+
- "Individual blog posts render with frontmatter data"
|
|
14
|
+
- "Tags are extracted and displayed for each post"
|
|
15
|
+
artifacts:
|
|
16
|
+
- path: "src/content/config.ts"
|
|
17
|
+
provides: "Content collection schema for blog posts"
|
|
18
|
+
exports: ["collections"]
|
|
19
|
+
- path: "src/pages/blog/[...slug].astro"
|
|
20
|
+
provides: "Dynamic blog post page template"
|
|
21
|
+
min_lines: 50
|
|
22
|
+
- path: "src/components/PostList.astro"
|
|
23
|
+
provides: "Reusable component for listing blog posts"
|
|
24
|
+
min_lines: 30
|
|
25
|
+
key_links:
|
|
26
|
+
- from: "src/pages/blog/[...slug].astro"
|
|
27
|
+
to: "getCollection('blog')"
|
|
28
|
+
via: "Astro content collections API"
|
|
29
|
+
pattern: "getCollection\\('blog'\\)"
|
|
30
|
+
- from: "src/content/config.ts"
|
|
31
|
+
to: "zod schema"
|
|
32
|
+
via: "defineCollection"
|
|
33
|
+
pattern: "defineCollection"
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<objective>
|
|
37
|
+
Implement blog content structure using Astro Content Collections with markdown posts, frontmatter validation, and tag support.
|
|
38
|
+
|
|
39
|
+
Purpose: Create the core blog functionality - content schema, post rendering, and listing capabilities.
|
|
40
|
+
Output: Content collection configured, sample post created, dynamic post template working.
|
|
41
|
+
</objective>
|
|
42
|
+
|
|
43
|
+
<context>
|
|
44
|
+
Block 01 completed: Astro project initialized with Tailwind and Vercel adapter.
|
|
45
|
+
Block 02 in progress: Layout system (runs parallel with this block).
|
|
46
|
+
|
|
47
|
+
This block focuses on the content layer using Astro's Content Collections API. Requirements specify:
|
|
48
|
+
- Markdown-based posts
|
|
49
|
+
- Tags for organization
|
|
50
|
+
- Syntax highlighting (shiki installed in Block 01)
|
|
51
|
+
|
|
52
|
+
Content Collections provide type-safe frontmatter, automatic routing, and built-in markdown processing. This is the canonical Astro approach for blog content.
|
|
53
|
+
|
|
54
|
+
Block 02 (layouts) and Block 03 (content) can run in parallel since they modify different files with no overlap.
|
|
55
|
+
</context>
|
|
56
|
+
|
|
57
|
+
<threads>
|
|
58
|
+
|
|
59
|
+
<thread type="auto">
|
|
60
|
+
<name>Thread 1: Configure content collection schema</name>
|
|
61
|
+
<files>src/content/config.ts, src/content/blog/sample-post.md</files>
|
|
62
|
+
<action>
|
|
63
|
+
Set up Astro Content Collections for blog posts:
|
|
64
|
+
|
|
65
|
+
1. Create src/content/config.ts with zod schema:
|
|
66
|
+
```ts
|
|
67
|
+
import { defineCollection, z } from 'astro:content';
|
|
68
|
+
|
|
69
|
+
const blog = defineCollection({
|
|
70
|
+
type: 'content',
|
|
71
|
+
schema: z.object({
|
|
72
|
+
title: z.string(),
|
|
73
|
+
description: z.string(),
|
|
74
|
+
pubDate: z.coerce.date(),
|
|
75
|
+
updatedDate: z.coerce.date().optional(),
|
|
76
|
+
tags: z.array(z.string()).default([]),
|
|
77
|
+
draft: z.boolean().default(false),
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const collections = { blog };
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
2. Create src/content/blog/ directory
|
|
85
|
+
|
|
86
|
+
3. Create sample post: src/content/blog/sample-post.md
|
|
87
|
+
- Frontmatter with all required fields (title, description, pubDate, tags)
|
|
88
|
+
- Markdown content with headings, paragraphs, code blocks
|
|
89
|
+
- Use tags: ["javascript", "astro", "web-development"]
|
|
90
|
+
- Include code block to test syntax highlighting
|
|
91
|
+
|
|
92
|
+
AVOID: Do not add complex media fields yet (images come later if needed). Do not add author field unless multi-author (single author blog). WHY: Keep schema minimal - only add fields when content requires them.
|
|
93
|
+
</action>
|
|
94
|
+
<verify>
|
|
95
|
+
Run: npm run build (should succeed with no schema errors)
|
|
96
|
+
Check: src/content/config.ts exports 'collections' with 'blog'
|
|
97
|
+
Check: sample-post.md exists with valid frontmatter
|
|
98
|
+
Run: npx astro check (validates content schema)
|
|
99
|
+
</verify>
|
|
100
|
+
<done>
|
|
101
|
+
- src/content/config.ts defines blog collection with zod schema
|
|
102
|
+
- Schema includes: title, description, pubDate, updatedDate, tags, draft
|
|
103
|
+
- sample-post.md created with valid frontmatter
|
|
104
|
+
- Build completes without content validation errors
|
|
105
|
+
</done>
|
|
106
|
+
</thread>
|
|
107
|
+
|
|
108
|
+
<thread type="auto">
|
|
109
|
+
<name>Thread 2: Create dynamic blog post template</name>
|
|
110
|
+
<files>src/pages/blog/[...slug].astro</files>
|
|
111
|
+
<action>
|
|
112
|
+
Create dynamic route for individual blog posts:
|
|
113
|
+
|
|
114
|
+
1. Create src/pages/blog/[...slug].astro
|
|
115
|
+
|
|
116
|
+
2. Implement getStaticPaths():
|
|
117
|
+
```ts
|
|
118
|
+
import { getCollection } from 'astro:content';
|
|
119
|
+
export async function getStaticPaths() {
|
|
120
|
+
const posts = await getCollection('blog', ({ data }) => !data.draft);
|
|
121
|
+
return posts.map(post => ({
|
|
122
|
+
params: { slug: post.slug },
|
|
123
|
+
props: post,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. Destructure props: const { data, render } = Astro.props;
|
|
129
|
+
4. Render post with BaseLayout:
|
|
130
|
+
- Pass title and description to layout
|
|
131
|
+
- Render metadata: title, date, tags
|
|
132
|
+
- Render content: const { Content } = await render();
|
|
133
|
+
- Style with Tailwind typography: prose dark:prose-invert max-w-3xl
|
|
134
|
+
|
|
135
|
+
5. Date formatting: use Intl.DateTimeFormat or date-fns
|
|
136
|
+
6. Tags: render as badges with Tag component (create in next thread)
|
|
137
|
+
|
|
138
|
+
AVOID: Do not add comments (requirements say "none"). Do not add related posts sidebar (keep minimal). WHY: Feature requirements explicitly exclude comments; related posts adds complexity without requirement.
|
|
139
|
+
</action>
|
|
140
|
+
<verify>
|
|
141
|
+
Run: npm run dev
|
|
142
|
+
Visit: http://localhost:4321/blog/sample-post
|
|
143
|
+
Check: Post renders with title, date, tags, and content
|
|
144
|
+
Check: Code blocks have syntax highlighting
|
|
145
|
+
Check: Prose typography applied correctly
|
|
146
|
+
</verify>
|
|
147
|
+
<done>
|
|
148
|
+
- [slug].astro implements getStaticPaths with getCollection
|
|
149
|
+
- Filters out draft posts (data.draft === false)
|
|
150
|
+
- Renders post content with proper typography
|
|
151
|
+
- Displays frontmatter data (title, date, tags)
|
|
152
|
+
- Syntax highlighting works in code blocks
|
|
153
|
+
</done>
|
|
154
|
+
</thread>
|
|
155
|
+
|
|
156
|
+
<thread type="auto">
|
|
157
|
+
<name>Thread 3: Build post list and tag components</name>
|
|
158
|
+
<files>src/components/PostList.astro, src/components/Tag.astro</files>
|
|
159
|
+
<action>
|
|
160
|
+
Create reusable components for displaying posts and tags:
|
|
161
|
+
|
|
162
|
+
PostList.astro:
|
|
163
|
+
1. Props interface: posts (array of blog collection entries)
|
|
164
|
+
2. Map over posts and display:
|
|
165
|
+
- Title (link to /blog/{slug})
|
|
166
|
+
- Description
|
|
167
|
+
- Date (formatted)
|
|
168
|
+
- Tags (using Tag component)
|
|
169
|
+
3. Sort by pubDate descending (newest first)
|
|
170
|
+
4. Style: space-y-8 for vertical spacing, hover states on titles
|
|
171
|
+
5. No pagination yet (add later if needed)
|
|
172
|
+
|
|
173
|
+
Tag.astro:
|
|
174
|
+
1. Props: tag (string), variant?: 'link' | 'badge'
|
|
175
|
+
2. If variant === 'link': render as link to /blog/tags/{tag}
|
|
176
|
+
3. If variant === 'badge': render as span with badge styles
|
|
177
|
+
4. Style: rounded-full px-3 py-1 text-sm bg-gray-200 dark:bg-gray-800
|
|
178
|
+
|
|
179
|
+
AVOID: Do not add filter/search UI (add later if needed). Do not add pagination yet (YAGNI - wait until many posts exist). WHY: Premature optimization - start simple, add features when content volume requires it.
|
|
180
|
+
</action>
|
|
181
|
+
<verify>
|
|
182
|
+
Check: PostList.astro accepts posts prop and maps over entries
|
|
183
|
+
Check: Tag.astro handles both 'link' and 'badge' variants
|
|
184
|
+
Check: Tags are clickable links (variant='link') or static badges
|
|
185
|
+
Run: grep -r "variant=" src/components/Tag.astro
|
|
186
|
+
</verify>
|
|
187
|
+
<done>
|
|
188
|
+
- PostList.astro displays posts with title, description, date, tags
|
|
189
|
+
- Posts sorted by date descending (newest first)
|
|
190
|
+
- Tag.astro renders tags as links or badges based on variant prop
|
|
191
|
+
- Hover states applied to post titles
|
|
192
|
+
</done>
|
|
193
|
+
</thread>
|
|
194
|
+
|
|
195
|
+
<thread type="checkpoint:human-verify" gate="blocking">
|
|
196
|
+
<what-built>
|
|
197
|
+
Complete blog content system with:
|
|
198
|
+
- Content collection schema (src/content/config.ts)
|
|
199
|
+
- Sample blog post (src/content/blog/sample-post.md)
|
|
200
|
+
- Dynamic post template ([slug].astro)
|
|
201
|
+
- PostList component (reusable post listing)
|
|
202
|
+
- Tag component (link and badge variants)
|
|
203
|
+
</what-built>
|
|
204
|
+
<how-to-verify>
|
|
205
|
+
1. cd /Users/jacweath/grid/blog && npm run dev
|
|
206
|
+
2. Visit http://localhost:4321/blog/sample-post
|
|
207
|
+
3. Check post page:
|
|
208
|
+
- Title displays correctly
|
|
209
|
+
- Date shows formatted (e.g., "January 23, 2024")
|
|
210
|
+
- Tags appear as badges or links
|
|
211
|
+
- Markdown content renders properly
|
|
212
|
+
- Code blocks have syntax highlighting (colors visible)
|
|
213
|
+
- Dark mode: check prose-invert class applies (text readable on dark bg)
|
|
214
|
+
4. Check browser console for errors (should be none)
|
|
215
|
+
5. Run: npm run build
|
|
216
|
+
- Build should complete successfully
|
|
217
|
+
- Check: npx astro check (no content schema errors)
|
|
218
|
+
6. Create test listing page:
|
|
219
|
+
- Add src/pages/blog/index.astro using PostList component
|
|
220
|
+
- Visit http://localhost:4321/blog
|
|
221
|
+
- Sample post appears in list
|
|
222
|
+
</how-to-verify>
|
|
223
|
+
<resume-signal>Type "approved" if content renders correctly and schema validates, or describe issues (e.g., "highlighting broken", "schema error", "tags not visible")</resume-signal>
|
|
224
|
+
</thread>
|
|
225
|
+
|
|
226
|
+
</threads>
|
|
227
|
+
|
|
228
|
+
<verification>
|
|
229
|
+
Complete verification checklist:
|
|
230
|
+
1. src/content/config.ts defines blog collection with zod schema
|
|
231
|
+
2. Schema includes all required fields (title, description, pubDate, tags, draft)
|
|
232
|
+
3. src/content/blog/sample-post.md exists with valid frontmatter
|
|
233
|
+
4. src/pages/blog/[slug].astro implements getStaticPaths correctly
|
|
234
|
+
5. Dynamic route filters out draft posts
|
|
235
|
+
6. Post template renders content with prose typography
|
|
236
|
+
7. Syntax highlighting works in code blocks
|
|
237
|
+
8. PostList.astro sorts posts by date (newest first)
|
|
238
|
+
9. Tag.astro supports 'link' and 'badge' variants
|
|
239
|
+
10. npm run build completes successfully
|
|
240
|
+
11. npx astro check passes with no errors
|
|
241
|
+
12. User approves content rendering and functionality
|
|
242
|
+
</verification>
|
|
243
|
+
|
|
244
|
+
<success_criteria>
|
|
245
|
+
- Content collection configured with type-safe schema
|
|
246
|
+
- Sample blog post renders at /blog/sample-post
|
|
247
|
+
- Frontmatter data displays correctly (title, date, tags)
|
|
248
|
+
- Markdown content styled with typography plugin
|
|
249
|
+
- Syntax highlighting functional
|
|
250
|
+
- PostList and Tag components work correctly
|
|
251
|
+
- Build succeeds with no content validation errors
|
|
252
|
+
- User approves rendering and functionality
|
|
253
|
+
</success_criteria>
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
---
|
|
2
|
+
cluster: james-weatherhead-blog
|
|
3
|
+
block: 04
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 3
|
|
6
|
+
depends_on: [02, 03]
|
|
7
|
+
files_modified: [src/pages/index.astro, src/pages/blog/index.astro, src/pages/about.astro, src/pages/blog/tags/[tag].astro]
|
|
8
|
+
autonomous: false
|
|
9
|
+
|
|
10
|
+
must_haves:
|
|
11
|
+
truths:
|
|
12
|
+
- "Homepage displays recent blog posts"
|
|
13
|
+
- "Blog index page lists all posts"
|
|
14
|
+
- "About page contains author information"
|
|
15
|
+
- "Tag pages filter posts by tag"
|
|
16
|
+
artifacts:
|
|
17
|
+
- path: "src/pages/index.astro"
|
|
18
|
+
provides: "Homepage with recent posts"
|
|
19
|
+
min_lines: 30
|
|
20
|
+
- path: "src/pages/blog/index.astro"
|
|
21
|
+
provides: "Complete blog post listing"
|
|
22
|
+
min_lines: 25
|
|
23
|
+
- path: "src/pages/about.astro"
|
|
24
|
+
provides: "About page with author info"
|
|
25
|
+
min_lines: 20
|
|
26
|
+
- path: "src/pages/blog/tags/[tag].astro"
|
|
27
|
+
provides: "Tag-filtered post listing"
|
|
28
|
+
min_lines: 40
|
|
29
|
+
key_links:
|
|
30
|
+
- from: "index.astro"
|
|
31
|
+
to: "getCollection('blog')"
|
|
32
|
+
via: "fetch recent posts"
|
|
33
|
+
pattern: "getCollection\\('blog'\\)"
|
|
34
|
+
- from: "[tag].astro"
|
|
35
|
+
to: "posts.filter"
|
|
36
|
+
via: "tag filtering"
|
|
37
|
+
pattern: "\\.filter.*tags\\.includes"
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<objective>
|
|
41
|
+
Create static pages (homepage, blog index, about) and tag-based filtering functionality.
|
|
42
|
+
|
|
43
|
+
Purpose: Connect layout system (Block 02) and content system (Block 03) into navigable pages with tag-based organization.
|
|
44
|
+
Output: Complete page structure with homepage, blog listing, about page, and tag filtering.
|
|
45
|
+
</objective>
|
|
46
|
+
|
|
47
|
+
<context>
|
|
48
|
+
Dependencies completed:
|
|
49
|
+
- Block 02: Layout system (BaseLayout, Header, Footer, DarkModeToggle)
|
|
50
|
+
- Block 03: Content system (blog collection, post template, PostList component)
|
|
51
|
+
|
|
52
|
+
This block brings everything together into actual pages users will visit. Navigation links from Header (Home, Blog, About) now have destinations. Tag filtering enables content organization without complex search.
|
|
53
|
+
|
|
54
|
+
This is where the blog becomes navigable and complete. After this block, users can:
|
|
55
|
+
- Land on homepage and see recent posts
|
|
56
|
+
- Browse all posts at /blog
|
|
57
|
+
- Read about the author at /about
|
|
58
|
+
- Filter posts by tag at /blog/tags/{tag}
|
|
59
|
+
</context>
|
|
60
|
+
|
|
61
|
+
<threads>
|
|
62
|
+
|
|
63
|
+
<thread type="auto">
|
|
64
|
+
<name>Thread 1: Build homepage and blog index</name>
|
|
65
|
+
<files>src/pages/index.astro, src/pages/blog/index.astro</files>
|
|
66
|
+
<action>
|
|
67
|
+
Create homepage and blog listing pages:
|
|
68
|
+
|
|
69
|
+
index.astro (Homepage):
|
|
70
|
+
1. Import BaseLayout, PostList, getCollection
|
|
71
|
+
2. Fetch recent posts:
|
|
72
|
+
```ts
|
|
73
|
+
const allPosts = await getCollection('blog', ({ data }) => !data.draft);
|
|
74
|
+
const recentPosts = allPosts
|
|
75
|
+
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
|
|
76
|
+
.slice(0, 5); // Show 5 most recent
|
|
77
|
+
```
|
|
78
|
+
3. Hero section:
|
|
79
|
+
- Site title: "James Weatherhead"
|
|
80
|
+
- Subtitle/tagline (e.g., "Software Engineer & Technical Writer")
|
|
81
|
+
- Brief intro paragraph (1-2 sentences)
|
|
82
|
+
4. Recent posts section:
|
|
83
|
+
- Heading: "Recent Posts"
|
|
84
|
+
- Use PostList component with recentPosts
|
|
85
|
+
- Link to /blog: "View all posts →"
|
|
86
|
+
5. Minimal design: max-w-3xl container, generous whitespace
|
|
87
|
+
|
|
88
|
+
blog/index.astro (Blog Index):
|
|
89
|
+
1. Import BaseLayout, PostList, getCollection
|
|
90
|
+
2. Fetch all posts (sorted by date descending)
|
|
91
|
+
3. Page title: "Blog"
|
|
92
|
+
4. Optional: display tag cloud (all unique tags)
|
|
93
|
+
5. Use PostList component with all posts
|
|
94
|
+
6. Style consistently with homepage
|
|
95
|
+
|
|
96
|
+
AVOID: Do not add complex hero animations (keep minimal). Do not add featured posts section yet (YAGNI). WHY: Requirements specify "minimal design" - focus on content, not embellishments.
|
|
97
|
+
</action>
|
|
98
|
+
<verify>
|
|
99
|
+
Run: npm run dev
|
|
100
|
+
Visit: http://localhost:4321 (homepage)
|
|
101
|
+
Check: Recent posts appear (up to 5)
|
|
102
|
+
Visit: http://localhost:4321/blog (blog index)
|
|
103
|
+
Check: All posts appear in list
|
|
104
|
+
Check: Both pages use BaseLayout with header/footer
|
|
105
|
+
</verify>
|
|
106
|
+
<done>
|
|
107
|
+
- index.astro displays hero section and 5 recent posts
|
|
108
|
+
- blog/index.astro lists all non-draft posts
|
|
109
|
+
- Both pages use consistent styling (max-w-3xl, Tailwind)
|
|
110
|
+
- "View all posts" link navigates to /blog
|
|
111
|
+
- Posts sorted by date descending
|
|
112
|
+
</done>
|
|
113
|
+
</thread>
|
|
114
|
+
|
|
115
|
+
<thread type="auto">
|
|
116
|
+
<name>Thread 2: Create About page</name>
|
|
117
|
+
<files>src/pages/about.astro</files>
|
|
118
|
+
<action>
|
|
119
|
+
Create About page with author information:
|
|
120
|
+
|
|
121
|
+
1. Create src/pages/about.astro with BaseLayout
|
|
122
|
+
2. Page title: "About"
|
|
123
|
+
3. Content structure:
|
|
124
|
+
- Heading: "About James Weatherhead"
|
|
125
|
+
- Bio paragraphs (placeholder text for now - user will customize):
|
|
126
|
+
* Professional background
|
|
127
|
+
* Technical interests
|
|
128
|
+
* Why they write (blog purpose)
|
|
129
|
+
- Contact information (if provided)
|
|
130
|
+
- Optional: Links to GitHub, LinkedIn, Twitter (if provided)
|
|
131
|
+
4. Style with prose typography: prose dark:prose-invert max-w-3xl
|
|
132
|
+
5. Consistent container width with other pages
|
|
133
|
+
|
|
134
|
+
Placeholder content example:
|
|
135
|
+
"I'm a software engineer passionate about web development, DevOps, and technical writing. This blog is where I share insights, tutorials, and lessons learned from building software."
|
|
136
|
+
|
|
137
|
+
AVOID: Do not add contact form (YAGNI - add if requested). Do not add photo upload (user can add later). WHY: Keep initial version simple - user can customize content after deployment.
|
|
138
|
+
</action>
|
|
139
|
+
<verify>
|
|
140
|
+
Visit: http://localhost:4321/about
|
|
141
|
+
Check: Page renders with heading and bio content
|
|
142
|
+
Check: Consistent styling with other pages (max-w-3xl, prose)
|
|
143
|
+
Check: Dark mode works correctly on about page
|
|
144
|
+
Check: Header navigation links to /about works
|
|
145
|
+
</verify>
|
|
146
|
+
<done>
|
|
147
|
+
- about.astro created with BaseLayout
|
|
148
|
+
- Page contains heading and placeholder bio content
|
|
149
|
+
- Prose typography applied for readability
|
|
150
|
+
- Consistent container width (max-w-3xl)
|
|
151
|
+
- Navigation link from Header works
|
|
152
|
+
</done>
|
|
153
|
+
</thread>
|
|
154
|
+
|
|
155
|
+
<thread type="auto">
|
|
156
|
+
<name>Thread 3: Implement tag-based filtering</name>
|
|
157
|
+
<files>src/pages/blog/tags/[tag].astro</files>
|
|
158
|
+
<action>
|
|
159
|
+
Create dynamic tag pages for filtering posts by tag:
|
|
160
|
+
|
|
161
|
+
1. Create src/pages/blog/tags/[tag].astro
|
|
162
|
+
|
|
163
|
+
2. Implement getStaticPaths to generate page for each unique tag:
|
|
164
|
+
```ts
|
|
165
|
+
import { getCollection } from 'astro:content';
|
|
166
|
+
export async function getStaticPaths() {
|
|
167
|
+
const allPosts = await getCollection('blog', ({ data }) => !data.draft);
|
|
168
|
+
const uniqueTags = [...new Set(allPosts.flatMap(post => post.data.tags))];
|
|
169
|
+
|
|
170
|
+
return uniqueTags.map(tag => {
|
|
171
|
+
const filteredPosts = allPosts.filter(post =>
|
|
172
|
+
post.data.tags.includes(tag)
|
|
173
|
+
);
|
|
174
|
+
return {
|
|
175
|
+
params: { tag },
|
|
176
|
+
props: { posts: filteredPosts },
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
3. Render tag page:
|
|
183
|
+
- Page title: "Posts tagged with '{tag}'"
|
|
184
|
+
- Display tag name prominently
|
|
185
|
+
- Show post count: "X posts"
|
|
186
|
+
- Use PostList component with filtered posts
|
|
187
|
+
- Breadcrumb: "Blog / Tags / {tag}"
|
|
188
|
+
- Link back to /blog
|
|
189
|
+
|
|
190
|
+
4. Update Tag component (from Block 03):
|
|
191
|
+
- When variant='link', link to /blog/tags/{tag}
|
|
192
|
+
- Ensure tags in PostList are clickable
|
|
193
|
+
|
|
194
|
+
AVOID: Do not add tag description field (YAGNI - tags are self-explanatory). Do not add "related tags" section (over-engineering). WHY: Keep tag pages simple - they're for filtering, not complex taxonomy.
|
|
195
|
+
</action>
|
|
196
|
+
<verify>
|
|
197
|
+
Run: npm run dev
|
|
198
|
+
Visit: http://localhost:4321/blog/tags/javascript (or any tag from sample post)
|
|
199
|
+
Check: Page shows only posts with that tag
|
|
200
|
+
Check: Post count displays correctly
|
|
201
|
+
Check: Tags in PostList are clickable and link to tag pages
|
|
202
|
+
Check: Breadcrumb or back link to /blog exists
|
|
203
|
+
Run: npm run build (should generate static page for each tag)
|
|
204
|
+
</verify>
|
|
205
|
+
<done>
|
|
206
|
+
- [tag].astro implements getStaticPaths with unique tags
|
|
207
|
+
- Filters posts by tag correctly
|
|
208
|
+
- Displays tag name and post count
|
|
209
|
+
- PostList shows filtered posts
|
|
210
|
+
- Tag component links to tag pages (variant='link')
|
|
211
|
+
- Build generates static page for each tag
|
|
212
|
+
</done>
|
|
213
|
+
</thread>
|
|
214
|
+
|
|
215
|
+
<thread type="checkpoint:human-verify" gate="blocking">
|
|
216
|
+
<what-built>
|
|
217
|
+
Complete page structure:
|
|
218
|
+
- Homepage (index.astro) with recent posts
|
|
219
|
+
- Blog index (blog/index.astro) with all posts
|
|
220
|
+
- About page (about.astro) with author info
|
|
221
|
+
- Tag pages (blog/tags/[tag].astro) with filtered posts
|
|
222
|
+
</what-built>
|
|
223
|
+
<how-to-verify>
|
|
224
|
+
1. cd /Users/jacweath/grid/blog && npm run dev
|
|
225
|
+
|
|
226
|
+
2. Test Homepage (http://localhost:4321):
|
|
227
|
+
- Hero section displays site title and intro
|
|
228
|
+
- Recent posts section shows up to 5 posts
|
|
229
|
+
- "View all posts" link navigates to /blog
|
|
230
|
+
- Header navigation visible (Home, Blog, About)
|
|
231
|
+
|
|
232
|
+
3. Test Blog Index (http://localhost:4321/blog):
|
|
233
|
+
- All blog posts listed
|
|
234
|
+
- Posts sorted newest first
|
|
235
|
+
- Tags visible and clickable
|
|
236
|
+
|
|
237
|
+
4. Test About Page (http://localhost:4321/about):
|
|
238
|
+
- Page renders with author info
|
|
239
|
+
- Consistent styling with other pages
|
|
240
|
+
- Navigation link from Header works
|
|
241
|
+
|
|
242
|
+
5. Test Tag Filtering:
|
|
243
|
+
- Click a tag from blog index or post page
|
|
244
|
+
- Should navigate to /blog/tags/{tag}
|
|
245
|
+
- Only posts with that tag appear
|
|
246
|
+
- Post count displays correctly
|
|
247
|
+
|
|
248
|
+
6. Test Navigation:
|
|
249
|
+
- Click each nav link (Home, Blog, About)
|
|
250
|
+
- All pages load without errors
|
|
251
|
+
- Dark mode toggle works on all pages
|
|
252
|
+
|
|
253
|
+
7. Run: npm run build
|
|
254
|
+
- Build completes successfully
|
|
255
|
+
- Check dist/ contains all static pages
|
|
256
|
+
</how-to-verify>
|
|
257
|
+
<resume-signal>Type "approved" if all pages work and navigation is functional, or describe issues (e.g., "tag filtering broken", "about page missing", "homepage posts not showing")</resume-signal>
|
|
258
|
+
</thread>
|
|
259
|
+
|
|
260
|
+
</threads>
|
|
261
|
+
|
|
262
|
+
<verification>
|
|
263
|
+
Complete verification checklist:
|
|
264
|
+
1. Homepage renders with hero and recent posts (max 5)
|
|
265
|
+
2. Blog index lists all non-draft posts sorted by date
|
|
266
|
+
3. About page displays author information
|
|
267
|
+
4. Tag pages filter posts correctly by tag
|
|
268
|
+
5. Navigation links work (Home, Blog, About)
|
|
269
|
+
6. Tags are clickable and link to tag pages
|
|
270
|
+
7. Dark mode works consistently across all pages
|
|
271
|
+
8. All pages use BaseLayout with header/footer
|
|
272
|
+
9. Responsive on mobile and desktop
|
|
273
|
+
10. npm run build generates all static pages
|
|
274
|
+
11. No console errors on any page
|
|
275
|
+
12. User approves page structure and navigation
|
|
276
|
+
</verification>
|
|
277
|
+
|
|
278
|
+
<success_criteria>
|
|
279
|
+
- Homepage displays recent posts and hero content
|
|
280
|
+
- Blog index shows complete post listing
|
|
281
|
+
- About page contains author information
|
|
282
|
+
- Tag filtering functional (click tag → see filtered posts)
|
|
283
|
+
- All navigation links work correctly
|
|
284
|
+
- Build generates all static pages
|
|
285
|
+
- Consistent styling and dark mode across all pages
|
|
286
|
+
- User approves overall page structure and UX
|
|
287
|
+
</success_criteria>
|