Revive blog posts as a content-typed gitsheets sheet (revises the deferred decision)
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- markdown, typescript
Research direction
Start by reading issue #44 and the deferred decision in specs/deferred.md, then inspect the existing gitsheet patterns and apps/api/scripts/import-laddr.ts. Trace apps/api/scripts/import-laddr/translators.ts and the referenced BlogPost schema and routing requirements. Done means the content-typed blog-post sheet, API, SPA routes, importer revival, and listed spec updates are implemented without the excluded workflows.
Written by the indexing model from the issue text.
Description
specs/deferred.md currently says blog posts get replaced by "staff-authored markdown files in the code repo at apps/web/src/content/blog/<slug>.md, shipped via PR." That decision predates gitsheets v1.2's content-typed records.
With v1.2 we can give blog posts their own gitsheets sheet — markdown bodies + TOML frontmatter — and get a better outcome than files-in-code-repo:
Why this beats the original deferral
| Concern | Files-in-code-repo | Content-typed sheet |
|---|---|---|
| PR-reviewable | ✅ | ✅ |
| Publish cadence | Tied to web deploys | Immediate on data-repo merge |
| Tags / cross-links | Ad-hoc frontmatter | Native TagAssignment |
| Author attribution | Hand-stamp in frontmatter | Native Person reference |
| Snapshot inclusion | Not in data snapshot | In the snapshot (pseudonymized) |
| API serving | Bespoke Vite handler | Existing read API pipeline |
/blog index cost |
Bundle every post into web build | queryAll({ withBody: false }) |
| laddr-import revival | Out of scope | Resurrect blog_posts table on the existing one-shot import |
Sheet shape
# .gitsheets/blog-posts.toml
[gitsheet]
root = 'blog-posts'
path = '${{ slug }}'
[gitsheet.format]
type = 'markdown'
body = 'body'
[gitsheet.schema]
$ref = './schemas/BlogPost.schema.json'
BlogPost entity (in packages/shared/src/schemas/blog-post.ts):
idUUIDv7legacyId(laddr'sBlogPost.ID, for the importer's idempotence)slug(kebab-case, slug-handle conventions)titlesummary(short markdown — stays in frontmatter)authorId→ PersonpostedAt(iso8601)editedAtnullablefeaturedImageKeynullable (attachment via gitsheets)deletedAtnullable (soft-delete)body(the markdown body — the designated content field)- standard
createdAt/updatedAt
Routing
Add to the SPA:
/blog— index (paginated, optional tag filter)/blog/:slug— detail/blog/tag/:namespace/:slug— tag-filtered (reuseTagsNamespacepattern)
API:
GET /api/blog-posts(list with facets, q, sort, page)GET /api/blog-posts/:slug(detail)POST/PATCH/DELETE— staff-only (per the original spec, blog wasn't a per-user-role CMS)
laddr-import revival
The existing one-shot importer at apps/api/scripts/import-laddr.ts currently skips blog_posts. Re-add it as another translator in apps/api/scripts/import-laddr/translators.ts:
- Map
BlogPost.Slug→slug(slugify-with-dedupe if invalid) - Map
BlogPost.Title→title - Map
BlogPost.Body→body - Map
BlogPost.AuthorID→ resolve via the existingidMaps.personByLegacy - Map
BlogPost.Published(and similar) →postedAt - Preserve
legacyIdso re-runs are idempotent
Sequencing
- Depends on #44 (content-typed gitsheets is the substrate) — or stand on its own as the first content-typed sheet in the project. Either order works since blog-posts is a brand-new sheet that doesn't conflict with the existing TOML-only ones.
- Sequenced after
cutover-prepso existing migration paths stay valid through cutover.
Spec updates needed
specs/deferred.md— update the "Blog (/blog) as a user-facing CMS" entry from "files in code repo" to "content-typed sheet, see this issue."- New spec files:
specs/api/blog.md,specs/screens/blog-index.md,specs/screens/blog-detail.md. specs/data-model.md— addBlogPostentity.specs/behaviors/legacy-id-mapping.md— note the newBlogPost.legacyIdaxis.
Out of scope: comments, reactions, the multi-author "posts under a topic" workflow — keep it as simple as the original deferral imagined.
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 1
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 25
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from CodeForPhilly/codeforphilly-ng
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
MarkdownEditor toolbar: use Radix Toolbar from radix-ui instead of the hand-rolled roving tabindex Openenhancement
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
All issues in CodeForPhilly/codeforphilly-ng
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
mksglu/context-mode#1200 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
jaegertracing/jaeger-ui#4506 ·
-
area:desktop area:ui bug platform:macos
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
anthropics/claude-code#96687 ·
-
good first issue
Difficulty 1/5 Under an hour Newbie friendliness 95/100
AOSSIE-Org/DebateAI#582 · 2 comments ·