CodeForPhilly / CodeForPhilly/codeforphilly-ng

Revive blog posts as a content-typed gitsheets sheet (revises the deferred decision)

Open
#45 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1
Forks
1
Avg merge
5d 3h
Merged PRs (30d)
9

Description

[specs/deferred.md](https://github.com/CodeForPhilly/codeforphilly-ng/blob/main/specs/deferred.md) currently says blog posts get replaced by "staff-authored markdown files in the code repo at `apps/web/src/content/blog/.md`, shipped via PR." That decision predates [gitsheets v1.2](https://github.com/JarvusInnovations/gitsheets/releases/tag/v1.2.0)'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

```toml
# .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`):
- `id` UUIDv7
- `legacyId` (laddr's `BlogPost.ID`, for the importer's idempotence)
- `slug` (kebab-case, slug-handle conventions)
- `title`
- `summary` (short markdown — stays in frontmatter)
- `authorId` → Person
- `postedAt` (iso8601)
- `editedAt` nullable
- `featuredImageKey` nullable (attachment via gitsheets)
- `deletedAt` nullable (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 (reuse `TagsNamespace` pattern)

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`](https://github.com/CodeForPhilly/codeforphilly-ng/blob/main/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 existing `idMaps.personByLegacy`
- Map `BlogPost.Published` (and similar) → `postedAt`
- Preserve `legacyId` so re-runs are idempotent

## Sequencing

- Depends on [#44](https://github.com/CodeForPhilly/codeforphilly-ng/issues/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-prep` so 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` — add `BlogPost` entity.
- `specs/behaviors/legacy-id-mapping.md` — note the new `BlogPost.legacyId` axis.

Out of scope: comments, reactions, the multi-author "posts under a topic" workflow — keep it as simple as the original deferral imagined.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.