product

Writing Experience — A1 design (author-facing manuscript editor)

Status: design / inventory complete (2026-07-02). Workstream A of COMPETITIVE_LANDSCAPE_STORY_SOFTWARE.md §5. Directive (user): best-in-class writing experience, built off the existing HiveJournal writing surfaces as much as possible — not a greenfield editor. Audience (D3, resolved): external creators (polished, self-serve).

The one-sentence thesis

We don't need a new editor app — we need to give the season/episode model (which already carries the bible, chapters, versioned prose, and AI refinement, and feeds the entire audiobook/screenplay/video pipeline) a creator-facing editor, and graft onto it the writing environment the journal composer already nails. Then we beat Sudowrite/Novelcrafter not on prose polish but on "consistency is free" — the bible is at hand, inline AI is bible-grounded, and JQ can answer questions about the open manuscript.

What the inventory found (the surfaces we build on)

Full inventory is in the PR that introduced this doc; the load-bearing facts:

  1. No rich-text editor library exists. Every writing surface is a plain <textarea> (journal composer, rehearsal room, chat) or a custom JSON editor (outline tree, graphic-novel panels). react-markdown is render-only. There is no TipTap/Lexical/Slate/ProseMirror dependency. → An editor-tech decision is required (see below).
  2. The journal entry composer (apps/frontend/src/app/dashboard/entries/new/page.tsx) is the best environment substrate: proven autosave (POST /api/journal at ≥10 chars), localStorage draft persistence, focus-mode chrome (auto-hide header while typing), font/size picker, tags, @mentions, and the write.cafe backdrop/word-count integration. All plain-textarea based.
  3. Chapter prose is VIEW-ONLY today. In the seasons admin, prose is produced by generate-take → critique → promote (ChapterTakesModal.tsx, ProseVersionsModal.tsx); there is no direct prose editing and no creator-facing surface at all. This is the core gap A1 fills.
  4. The refinement + history machinery already exists and is reusable wholesale: chapter_takes (draft/live/archived generations), the critique loop (episode-take-critiques), and prose_versions (migration 321 — full before/after audit with operation incl. a manual_edit value already defined). prose_versions is a ready-made manuscript edit-history spine.
  5. Bible + canon intelligence is doneStoryBibleModal.tsx with the Current/Editor/History tabs plus the new 📍 Mentions / 📈 Progression / 🔗 Relationships read-layer tabs, and JQ canon mode (ask/edit canon conversationally, prose-grounded canon_health). This is the "bible at hand."
  6. Shared plumbing: apiRequest + Supabase bearer auth (lib/api.ts); Tailwind with the ember/emberkiln palette; ui/Textarea, ui/Button, etc.

The two real decisions — RESOLVED (2026-07-02)

  • D-A → Season/episode model. A manuscript is a season; chapters are story_episodes.chapter_prose.
  • D-B → Adopt TipTap now (not textarea-v1). The user chose best-in-class from the start. TipTap (ProseMirror) is the editor substrate. Constraint: the pipeline (audio/screenplay/scene) reads chapter_prose as plain text, so the editor must serialize to plain text / lightweight markdown on save — TipTap is the editing UX, chapter_prose stays plain-text canonical.
  • Sequencing: external-creator ownership — ✅ shipped (A1a-auth). The investigation found story_seasons.owner_user_id already exists (migration 134) AND the studio/manuscript create path already sets it to the uploader (manuscript.ts:359) — so no migration was needed. The gap was only that the new prose endpoints (:episodeId/:seasonId params) bypassed the existing router.param('id') ownership gate. Fixed with season-access.ts (canEditSeasonProse, strict own-or-super, golden-tested) + an assertSeasonProseAccess guard on GET/PATCH prose + prose-assist. RLS-policy rewrite deferred — it's dormant + not the enforcement layer (frontend never hits these tables directly; backend uses the service-role client), so it's defense-in-depth for a future direct-client path, not a blocker.

D-A. Data home — season/episode model (chosen) vs. journal notebook

  • Recommendation: the season/episode model. A manuscript IS a season; chapters ARE story_episodes.chapter_prose. It already has the bible, the version history (prose_versions), the AI refinement (takes/critique), and — critically — it is what the audiobook/screenplay/scene pipeline reads. Writing here means the creator's words flow straight into the moat. The journal notebook path (a manuscript notebook type) is simpler but a dead end: it doesn't connect to the bible or the production pipeline, so we'd rebuild all of that later.
  • Consequence: A1 needs creator ownership + RLS on seasons (today seasons are super-admin/creator-gated). External creators own their seasons. This is the main net-new backend work.

D-B. Editor tech — TipTap (chosen)

  • Chosen: TipTap now. ProseMirror-based, React bindings, extensible (the selection-toolbar + inline-AI extensions in A2 hang off it cleanly, and ghost-text autocomplete becomes possible later). Reuse the journal composer's environment (autosave cadence, focus-mode chrome, fonts) around it.
  • Serialization constraint: chapter_prose stays plain text (the audio/screenplay/scene pipeline reads it). Configure TipTap with a minimal StarterKit and serialize to plain text / lightweight markdown on save; hydrate the editor from that text on load. The rich layer is editing-time only.

A1 architecture (reuse map)

CapabilityReuseNet-new
Writing environment (autosave, draft, focus chrome, fonts, word count)journal composer patterns + write.cafe pilllift into a creator editor route
Manuscript + chapters datastory_seasons / story_episodes.chapter_prosecreator ownership + RLS
Direct prose editing (the gap)PATCH prose + prose_versions (manual_edit)a save endpoint that writes prose + a version row
Edit history / restoreprose_versions + ProseVersionsModal patternssurface it creator-side
Bible at handStoryBibleModal + Mentions/Progression/Relationships tabsdock it as an editor side panel
Inline AI actions (rewrite/expand/continue/describe)the LLM shim + bible context (buildBiblePromptBlock)selection→action endpoint + floating toolbar
Deeper refinementchapter_takes + critique loopcreator-facing entry points
Chat-with-your-manuscriptJQ canon mode (already season-scoped!)scope JQ to the open chapter + a "ask about this" affordance
Auth/API/design systemapiRequest, Tailwind ember palette, ui/*

Phasing

  • A1a — Direct prose editing (TipTap).Shipped (first slice): a TipTap editor (ProseEditorModal.tsx) bound to a chapter's live prose, opened via "✏ Edit prose" in ChapterTakesModal. GET/PATCH /api/story-seasons/episodes/:id/prose — the PATCH snapshots a versioned manual_edit (mirrors promote-take, revertable from Prose History), writes chapter_prose, and best-effort rebuilds audio segments. Serializes TipTap → plain text on save so the pipeline stays happy. Gated to current creator/super-admin access.Creator route shipped: external creators reach the editor on their own stories via /studio/write (writer hub — lists the caller's seasons via GET /api/story-seasons/mine) → /studio/write/[seasonId] (chapter list via the owner-gated GET /api/story-seasons/:seasonId/editor-chapters) → opens ProseEditorModal per chapter. All owner-gated (assertSeasonProseAccess, own-or-super) — no /dashboard/admin needed. A "Write" nav item lives in the Studio chrome. (External-creator ownership/RLS on seasons already shipped as A1a-auth.) ✅ Composer environment shipped (A1a-env): debounced autosave (1.5s idle; client-side unchanged check skips redundant PATCH/version snapshots; close flushes dirty content; ⌘/Ctrl-S force-saves) + a save-status pill · a distraction-free focus mode (hides bible + Voice/Ask JQ/Bible chrome, narrows to a centered measure; Esc exits) · serif/sans + size controls persisted in localStorage. A1a is complete.Chapter management shipped: the creator surface can add / rename / reorder / delete chapters (not just edit imported ones) — in-editor ‹ N of M › navigation (flush-saves before switching) + owner-gated endpoints POST /:seasonId/chapters, PATCH /episodes/:episodeId/chapter-meta, DELETE /episodes/:episodeId/chapter (delete + contiguous renumber; FK children cascade), PATCH /:seasonId/chapters/order (two-pass renumber). New chapters use the manuscript-import convention (triggered+public) so they flow through render/publish uniformly. No migration.
  • A1b — Bible side panel.Shipped: BibleReferencePanel.tsx docked in the editor (toggle "📖 Bible") — a scannable Characters/Settings reference (name, physical, current state, voice tics, relationships) with a filter and click-a-name-to-insert-at-cursor (spelling consistency). The Novelcrafter "codex at hand" feature. Reference-only; the full audit tabs (Mentions / Progression / Relationships) + editing stay in the Story Bible modal + JQ canon mode.
  • A2 — Inline AI actions.Shipped: InlineAiToolbar.tsx — select a passage → a floating toolbar offers Rewrite / Expand / Describe / Continue; Rewrite/Expand/Describe replace the selection, Continue appends. Backend POST /api/story-seasons/:seasonId/prose-assist grounds every action in the bible via buildBiblePromptBlock (claude-haiku-4-5, feature_key prose_assist.<action>). Nothing persists until the writer saves. The inline, low-latency counterpart to the chapter critique loop.
  • A3 — Chat-with-your-manuscript.Shipped: a "💬 Ask JQ" button in the editor dispatches the existing jq:open-canon event with this story's seasonId → JQ opens in canon mode (bible-grounded), above the editor (z-1001), so the writer can chat while writing. If a passage is selected, it's passed as a seed message (Chatbot pre-fills the input). ✅ Deepened + creator-unlocked: canon mode was super-admin-only (Phase 1); it's now own-or-super, so external creators get JQ grounded in their own story — read, the canon edit/health tools, AND the open chapter's live prose injected into context (canon_episode_id scope, migration 447; validated to belong to the scoped season so no cross-story leak). "What did Evelyn know here?" now reads the actual chapter, not just the bible. Gating flows through a new canAccessCanonScope (chat create + generateChatResponse + executeCanonTool); universe canon stays super-admin-only.
  • A4 — Prose-quality bar.Investigated — see PROSE_QUALITY_BENCHMARK.md. Decision: don't chase a "beat Muse on one paragraph" engine rewrite (a losing bet vs a fiction-tuned model); instead ship Style Examples (voice exemplar) + a sentence-rhythm critic, stand up an eval harness to choose the draft-model tier from data, and keep the consistency + pipeline moat. Recommendations filed as tasks.

Competitive framing

Don't try to out-prose Sudowrite's Muse head-on (their whole company). Win on what our architecture makes free: the bible is live, inline AI is bible-grounded, and JQ answers questions about the open manuscript — because we generate from canon rather than bolting it on. That is a writing-experience advantage the drafting tools structurally can't match. See the competitive doc §1.

Open questions / risks

  • Creator ownership model. Seasons are creator/super-admin gated today. External-creator ownership + RLS + per-user quotas is the biggest net-new piece and should be specced before A1a. (Ties to the BYOK/model-choice item in §4.)
  • Migration surface. Direct-edit + versions reuse existing tables; ownership likely needs a migration (owner_user_id on seasons if absent + RLS).
  • Two front doors. Journaling (consumer) and manuscript drafting (creator) are different products sharing an environment. Keep the creator editor its own route; don't overload the journal composer.
  • TipTap v2 trigger. Reassess after A2 — if selection-UX or ghost-text autocomplete is limiting, adopt TipTap (plain-text/markdown serialization keeps data compatible; no migration).
WRITING EXPERIENCE A1 — Docs | HiveJournal