Content as the first EntryDimension #1

Closed
opened 2026-08-26 09:12:45 +00:00 by GKaszewski · 0 comments
Owner

What to build

The tracer slice for ADR 0002. MoodEntry stops carrying Content as a field; Content becomes a self-contained EntryDimension with its own module, table, port, and mapper, composed onto the entry at read time.

This slice's real deliverable is the pattern, not the feature — every dimension after this one copies it. Get the composition layer right here and the rest are mechanical.

Reads must batch-load a dimension across a whole page of entries, not per entry. The existing entry hydration already does this for side tables; the composer generalises it so adding a dimension does not widen an existing function's signature.

The wire format is a kind-keyed map (ADR 0002, and the SPA's Zod schemas are hand-written, so a discriminated union covers it). The server dispatches by kind to a typed parser — a new dimension is a new module plus one registration line, never an edit to existing dimension code.

Acceptance criteria

  • MoodEntry no longer has a content field or set_content
  • Content has its own domain module, table, port, and mapper
  • A composer assembles dimensions onto entries, batch-loading per dimension for a page of entries
  • Adding a hypothetical second dimension requires no edit to Content's module or to MoodEntry
  • Wire format is kind-keyed; server dispatches by kind to a typed parser
  • SPA reads and writes content through the new shape with Zod validation intact
  • Existing entry tests pass; entry creation, update, listing, and calendar are unaffected from the outside
  • Creating and updating an entry with content remains a single atomic request

Blocked by

None - can start immediately.

## What to build The tracer slice for ADR 0002. `MoodEntry` stops carrying `Content` as a field; `Content` becomes a self-contained `EntryDimension` with its own module, table, port, and mapper, composed onto the entry at read time. This slice's real deliverable is **the pattern**, not the feature — every dimension after this one copies it. Get the composition layer right here and the rest are mechanical. Reads must batch-load a dimension across a whole page of entries, not per entry. The existing entry hydration already does this for side tables; the composer generalises it so adding a dimension does not widen an existing function's signature. The wire format is a kind-keyed map (ADR 0002, and the SPA's Zod schemas are hand-written, so a discriminated union covers it). The server dispatches by kind to a typed parser — a new dimension is a new module plus one registration line, never an edit to existing dimension code. ## Acceptance criteria - [ ] `MoodEntry` no longer has a `content` field or `set_content` - [ ] `Content` has its own domain module, table, port, and mapper - [ ] A composer assembles dimensions onto entries, batch-loading per dimension for a page of entries - [ ] Adding a hypothetical second dimension requires no edit to `Content`'s module or to `MoodEntry` - [ ] Wire format is kind-keyed; server dispatches by kind to a typed parser - [ ] SPA reads and writes content through the new shape with Zod validation intact - [ ] Existing entry tests pass; entry creation, update, listing, and calendar are unaffected from the outside - [ ] Creating and updating an entry with content remains a single atomic request ## Blocked by None - can start immediately.
GKaszewski added the ready-for-human label 2026-08-26 09:12:45 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: GKaszewski/k-mood#1