Composition Mode - curated ordering + prose blocks inside a motif #128

Closed
opened 2026-07-15 20:35:38 +00:00 by claude-bot · 2 comments
Contributor

Feature idea (Fable ideation, rank #5) · Effort: M · Value: High

Pitch
Let her arrange a motif's entries in narrative order and interleave short prose/heading blocks, so the published page and Markdown export read as an essay with evidence rather than a reverse-chron dump.

Why it matters
Publishing exists (public motif pages, embeds, all-motifs MD export) but every rendering orders entries ORDER BY e.created_at DESC (verified in routes/public.ts ~45/315/430 and routes/export.ts). A researcher publishing an argument needs to control sequence: setup → evidence → counter-evidence → conclusion. Right now her newest stray note sits at the top of her published essay.

How it fits
entry_motifs (005_motifs_connections.sql) has no position column - add position INTEGER plus a small motif_blocks table (motif_id, position, content, format) reusing content_format conventions from migration 031 and write-sanitization. Public renderer and MD exporter switch to ORDER BY position NULLS LAST, created_at DESC so uncurated motifs behave exactly as today. Drag-to-reorder in the existing motif detail UI.

Why it was likely missed
Motifs shipped as buckets in Phase 5; publishing (v5/v6) shipped as rendering the bucket. The document-ness of a published motif - that it's an authored artifact, not a query result - fell between those two phases.

Acceptance criteria

  • Entries within a motif can be manually ordered; order persists.
  • Prose/heading blocks can be interleaved between entries.
  • Public pages and MD export honor the curated order; uncurated motifs are unchanged.

From the 2026-07-15 codebase audit - Fable feature-ideation pass. Companion report: docs/.internal/report-2026-07-15.md (gitignored).

**Feature idea** (Fable ideation, rank #5) · **Effort:** M · **Value:** High **Pitch** Let her arrange a motif's entries in narrative order and interleave short prose/heading blocks, so the published page and Markdown export read as an essay with evidence rather than a reverse-chron dump. **Why it matters** Publishing exists (public motif pages, embeds, all-motifs MD export) but every rendering orders entries `ORDER BY e.created_at DESC` (verified in `routes/public.ts` ~45/315/430 and `routes/export.ts`). A researcher publishing an argument needs to control sequence: setup → evidence → counter-evidence → conclusion. Right now her newest stray note sits at the top of her published essay. **How it fits** `entry_motifs` (`005_motifs_connections.sql`) has no position column - add `position INTEGER` plus a small `motif_blocks` table (`motif_id, position, content, format`) reusing `content_format` conventions from migration 031 and write-sanitization. Public renderer and MD exporter switch to `ORDER BY position NULLS LAST, created_at DESC` so uncurated motifs behave exactly as today. Drag-to-reorder in the existing motif detail UI. **Why it was likely missed** Motifs shipped as buckets in Phase 5; publishing (v5/v6) shipped as rendering the bucket. The document-ness of a published motif - that it's an authored artifact, not a query result - fell between those two phases. **Acceptance criteria** - [ ] Entries within a motif can be manually ordered; order persists. - [ ] Prose/heading blocks can be interleaved between entries. - [ ] Public pages and MD export honor the curated order; uncurated motifs are unchanged. --- _From the 2026-07-15 codebase audit - Fable feature-ideation pass. Companion report: `docs/.internal/report-2026-07-15.md` (gitignored)._
Author
Contributor

Picking this up on feat/v10-composition (targeting v9.4.0).

Design:

  • Migration 056: entry_motifs.position INTEGER (nullable) + motif_blocks (id, motif_id, position, content, content_format) reusing the migration-031 plain/markdown convention.
  • One shared ordered sequence: entries and prose blocks both carry position. All renderers switch to ORDER BY em.position NULLS LAST, e.created_at DESC and interleave blocks by position — so an uncurated motif renders exactly as today (all-NULL positions → created_at DESC, no blocks).
  • Endpoints on /api/motifs/:id: extend detail with position + a blocks array; PUT /composition (transactional reorder of the full mixed list), POST/PATCH/DELETE /blocks (sanitize on write). Writes gate on ACCESSIBLE_PROJECTS_SQL like the existing entry-add.
  • Renderers updated: routes/public.ts (3 sites) + routes/export.ts (MD) honor position + interleave blocks.
  • Frontend: drag-to-reorder in the motif detail curate view + add/edit/delete prose blocks — native HTML5 DnD (no new dependency).

Will report when the core lands.

Picking this up on `feat/v10-composition` (targeting v9.4.0). Design: - Migration 056: `entry_motifs.position INTEGER` (nullable) + `motif_blocks (id, motif_id, position, content, content_format)` reusing the migration-031 `plain`/`markdown` convention. - One shared ordered sequence: entries and prose blocks both carry `position`. All renderers switch to `ORDER BY em.position NULLS LAST, e.created_at DESC` and interleave blocks by position — so an **uncurated motif renders exactly as today** (all-NULL positions → created_at DESC, no blocks). - Endpoints on `/api/motifs/:id`: extend detail with `position` + a `blocks` array; `PUT /composition` (transactional reorder of the full mixed list), `POST/PATCH/DELETE /blocks` (sanitize on write). Writes gate on `ACCESSIBLE_PROJECTS_SQL` like the existing entry-add. - Renderers updated: `routes/public.ts` (3 sites) + `routes/export.ts` (MD) honor position + interleave blocks. - Frontend: drag-to-reorder in the motif detail curate view + add/edit/delete prose blocks — native HTML5 DnD (no new dependency). Will report when the core lands.
Author
Contributor

Done — shipped in v9.4.0 (PR #143, merge 48c0048).

Migration 056 (entry_motifs.position + motif_blocks); motif detail returns position + blocks; PUT /composition (transactional reorder) + POST/PATCH/DELETE /blocks (sanitized). All renderers — public page JSON, motif Atom feed, Markdown export — use ORDER BY position NULLS LAST, created_at DESC and the page/export interleave prose blocks. Frontend: "Compose" mode with native drag-reorder (▲/▼ touch fallback) + block editing; read + public views render the interleaved composition. 5 integration tests.

Acceptance: entries manually orderable, order persists · prose/heading blocks interleaved between entries · public pages + MD export honor curated order, uncurated motifs unchanged (verified by a dedicated test).

Note: one bug caught in review before merge — the public-page entries query had em.position in GROUP BY/ORDER BY but not the SELECT, so public entries lacked their position; fixed.

Done — shipped in **v9.4.0** (PR #143, merge `48c0048`). Migration 056 (`entry_motifs.position` + `motif_blocks`); motif detail returns `position` + `blocks`; `PUT /composition` (transactional reorder) + `POST/PATCH/DELETE /blocks` (sanitized). All renderers — public page JSON, motif Atom feed, Markdown export — use `ORDER BY position NULLS LAST, created_at DESC` and the page/export interleave prose blocks. Frontend: "Compose" mode with native drag-reorder (▲/▼ touch fallback) + block editing; read + public views render the interleaved composition. 5 integration tests. **Acceptance:** ✅ entries manually orderable, order persists · ✅ prose/heading blocks interleaved between entries · ✅ public pages + MD export honor curated order, **uncurated motifs unchanged** (verified by a dedicated test). *Note:* one bug caught in review before merge — the public-page entries query had `em.position` in `GROUP BY`/`ORDER BY` but not the `SELECT`, so public entries lacked their position; fixed.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/TeaLeaves#128
No description provided.