Stream Facility
Development Log
A running record of what's been built, what's underway, and what's on the horizon. Updated as the platform grows.
Last updated 2026-09-01
99
Features built
0
In progress
5
Planned
Author Manuscript Sales
Authors can now sell access to their manuscripts directly on the platform. Set a price, switch the draft to Paid access mode, and readers purchase through Stripe Checkout — proceeds transfer automatically to the author's connected payout account, minus the platform commission.
- ·Authors set a price and access mode on any Studio draft — no Stripe product setup needed; checkout sessions are built dynamically using Stripe price_data
- ·Readers see the chapter and scene structure as a teaser; scene content is gated until purchase
- ·Stripe Checkout routes proceeds directly to the author's Connect account at the moment of charge via transfer_data — no manual payout steps
- ·Webhook confirms each purchase and writes to the manuscript_purchases collection; all access checks on the book page and scene API query it
- ·Authors must have a verified, charges-enabled Stripe Connect account before their draft can be purchased — an amber prompt guides them through setup if not
- ·Platform-wide feature flag (manuscriptSalesEnabled) lets admins enable or disable manuscript sales without a deploy
Author Payout Accounts (Stripe Connect)
Authors can connect a Stripe Express account from Dashboard → Billing to receive proceeds from manuscript sales. Stripe handles identity verification, bank details, and tax documents. The platform retains a configurable commission; the remainder transfers automatically at the moment each sale completes.
- ·One-click setup from Dashboard → Billing — Stripe Express account created and the standard Stripe onboarding flow opened immediately
- ·Three states surfaced in the UI: not connected (Set up payouts), verification pending (Complete setup), and active (Manage payouts →)
- ·Active accounts link to the Stripe Express dashboard for payout history, bank management, and tax documents (W-9 / 1099 handled by Stripe)
- ·Commission rate is admin-configurable (default 15%); Stripe applies the split automatically at charge time — no manual transfers or ledger entries needed
- ·A verified, charges-enabled account is required before a manuscript can go on sale — authors are prompted if setup is incomplete
Studio — Collaborator Plan View & Scene Discussion Threads
Collaborators can open shared drafts in a read-only Plan view — seeing the full act/chapter/scene structure, word counts, and prose previews — and leave threaded discussion notes on individual scenes for the draft owner to review, resolve, and act on.
- ·The Plan view is fully visible to collaborators: act and chapter structure, scene list, word counts, and prose previews
- ·All structural controls are locked for collaborators — no drag-and-drop reordering, scene creation, renaming, or deletion
- ·Each scene has a ✎ discussion button that opens an inline threaded discussion panel scoped to that scene
- ·Discussion threads support nested replies; any participant can reply to any top-level note
- ·Draft owners can mark a thread as resolved (it collapses into a summary count) and reopen it at any time
- ·Collaborators and the draft owner can delete their own notes; the draft owner can delete any note in the draft
World Collaboration — Invite Trusted Authors to Co-Create
World owners can now invite trusted authors as collaborators. Collaborators gain read access to the world's codex, series, and books, a read-only Studio Plan view of linked drafts, and a full scene-level discussion thread system for structured feedback — without any commercial arrangement or license setup required.
- ·Invite any author to your world from world settings — they receive a notification and accept to become an active collaborator
- ·Collaborators can browse all codex entries (characters, locations, lore, and all other entity types) as read-only reference
- ·Shared drafts appear in the collaborator's Studio under a 'Shared With Me' section, linking directly to the read-only Plan view
- ·Collaborators can read the full manuscript of any book linked to the shared world, the same way beta readers do
- ·Collaboration does not count toward a collaborator's draft limit — shared drafts are naturally excluded from the draft quota
Content Reporting & Moderation — Safety Tools for Readers and Authors
Readers can now flag content directly from the manuscript view, and authors whose content is reviewed by the team will receive clearer, reason-specific notifications and see an informative banner on their dashboard — with a straightforward path to dispute any decision they believe was made in error.
- ·A Report button appears at the end of each scene while reading, opening the report form with the content location already filled in (world, series, book, chapter, and scene)
- ·Moderation actions now carry a specific reason — such as a copyright claim, a policy concern, or a content safety issue — which is reflected in the notification sent to the author and the banner shown on their dashboard
- ·Authors whose content is reviewed receive a clear banner on the relevant dashboard page explaining the action, with a built-in dispute form
- ·Individual scenes can be hidden by the moderation team without affecting the rest of the manuscript — hidden scenes appear as locked in the reading tree while the author retains access to review and correct the content
- ·The book dashboard surfaces a count of any hidden scenes and prompts the author to review and address them
Write Assist — Smarter Scene Awareness
The Write Assist panel now shows you exactly which chapter and scene you are working on, matching the display in the AI Analyse panel. When you switch to a different scene, the panel resets cleanly — no leftover output or settings from the previous scene carry over.
- ·Chapter and scene name displayed below the Write Assist header (e.g., Chapter 2 · Scene 3)
- ·Switching scenes resets the panel output so you always start fresh in the new scene
- ·Prompt variables saved for a scene are restored when you return to it later
Studio — Codex Quick-Reference Panel
The Write view now has a Codex panel that lets you look up any world entity without leaving the manuscript. Search across characters, locations, and all other entity types while you write, and tap any result to see a full detail card — portrait, bio, relationships, and more — automatically scoped to the era of the scene you are in.
- ·Open via the ⊛ Codex button in the Write toolbar — panel sits alongside the manuscript in the right sidebar
- ·Instant search filters across all entity types as you type; matching groups expand automatically
- ·Detail card shows portrait, role, bio, relationships, and life status — era-aware so the information matches the scene's timeline position
- ·Direct link from each entity to its full codex page for deeper reference
- ·Eras strip shown when no search is active, giving a quick world timeline overview
Studio — Author Notes
You can now attach private notes to individual paragraphs in any scene. Notes are anchored to the exact paragraph — they stay in place as you edit above or below, and survive reordering. Open the Notes panel from the Write toolbar to see all your notes for the draft in manuscript order.
- ·Click any paragraph while the Notes panel is open to add or view a note for that paragraph
- ·Notes panel shows all draft notes sorted by chapter, scene, and paragraph position
- ·Click a note's location label to jump directly to that paragraph in the manuscript
- ·Notes are private — only the author can see them, never shown to beta readers
- ·Collapse notes to a one-line preview to keep the panel tidy during long writing sessions
Community Events — Tier-Gated Event Listings
Stream members can now list readings, book launches, workshops, and other events on the platform. Submissions are tier-gated (Free=1 active listing, Indie=3, Pro=unlimited) and auto-expire when the event date passes — no cron job or admin cleanup required. A public /events page groups upcoming events by month with full venue and author details.
- ·community_events collection: title, type, date (YYYY-MM-DD), url, description, optional location, authorId
- ·Five event types: Reading, Book Launch, Workshop, Virtual Event, Other — location field only collected for non-virtual types
- ·Tier limits enforced at POST time by counting active (date >= today) events; past events count as zero and slots recycle automatically
- ·Dashboard management page at /dashboard/community-events: tier usage bar, conditional location field, expired listings shown faded, limit-reached state replaces the form
- ·Public /events page groups events by month with a date block, type badge, formatted location, author credit, and external link
- ·Events tab in Local & You shows upcoming events with a Submit button for logged-in users and a View all link to /events
- ·Events link added to the outside nav between Authors and Search
Community Roles — Editor, Publisher, Influencer, Podcaster
Authors can now register professional roles beyond writing — editor, publisher, influencer channel, and podcast channel — with multi-entry external links and a per-role opt-in toggle for Local & You discovery. This replaces the earlier plan for separate account types; all users share a single account.
- ·Four role types stored as flat fields on the authors document: editorLinks[], publisherLinks[], influencerLinks[], podcasterLinks[] (LinkPair arrays), plus four boolean showInLocalAndYou flags
- ·RoleSection component in the profile page: LinkPairList for multi-entry links using the existing add-button pattern, plus a toggle switch that only renders when the role has at least one link
- ·Toggle visible only when links.length > 0 — prevents users from opting into Local & You with an empty listing
- ·Local & You Publishers, Editors, and Voices tabs query the corresponding showInLocalAndYou flag alongside a non-empty links check
- ·GET and PUT /api/authors/me extended to include all 8 new fields
Local & You — Tabbed Community Discovery Hub
The Local & You lobby wing now leads to a tabbed discovery hub at /local-and-you, separate from the plain author listing at /authors. Five tabs cover Authors, Publishers, Editors, New Voices, and Events. The top-nav Authors link and /authors route remain unchanged as a direct directory shortcut.
- ·New /local-and-you route: server component, five tabs driven by ?tab= URL param, all tab data fetched in parallel
- ·/authors reverted to a simple authors grid — no tabs, no session required — preserving the nav link destination
- ·Lobby wing card for Local & You updated to href /local-and-you
- ·Tab nav uses plain Link elements with ?tab= params — overflow-x: auto removed to prevent browser scroll-arrow controls from appearing
- ·Authors tab: authors with at least one public book or world; Publishers/Editors/Voices tabs: role opt-in queries; Events tab: upcoming community events with author join
- ·Events tab shows a Submit button for logged-in users and a View all → link to /events
Stream Facility Studio v2.0.2 — Sync & Nav Fixes
Studio v2.0.2 fixes manuscript sync and the in-app navigation. Pulling from the cloud now correctly downloads your acts, chapters, and scenes — previously they were silently skipped because Studio sub-documents are indexed by draft, not by author. The nav is also cleaner: web-only links (Lobby, Library, Beta) are hidden inside the app, and the Help link goes to the live help centre.
- ·story_acts, story_chapters, and story_scenes now sync correctly — the pull endpoint fetches them via draftId rather than authorId, which is how Studio structures its data
- ·Desktop nav hides web-only items (Lobby, Library, Beta, Inbox, Notifications) and shows a Sync shortcut instead of the download page
- ·Pro plan badge displays correctly on first launch before the first sync, using a local fallback rather than an empty author query
- ·Sign Out in the desktop app now calls the Tauri IPC command directly instead of routing through NextAuth
- ·Sync error messages are now surfaced properly — a crash in the write loop previously returned an empty response that showed as a JSON parse error
Stream Facility Studio — Phase 5: AI Settings & Local Providers
AI provider configuration is now built into the Studio app at /dashboard/desktop/ai. Choose Anthropic, OpenAI, or OpenRouter and paste an API key once — it's stored in the OS keychain and never written to disk or sent to the platform. Ollama and LocalAI users get automatic local-server detection at startup with a live re-detect button in settings.
- ·Five providers: Anthropic, OpenAI, OpenRouter (cloud) + Ollama, LocalAI (local — no API key required)
- ·API keys stored in OS keychain via Tauri keyring crate — never in SQLite, never in any plaintext file
- ·At Studio startup, local AI servers are automatically detected; local providers configure without any API key required
- ·Key presence is exposed as a boolean flag only — actual key values are never returned to the UI; a live re-detect option is available on demand
- ·aiProvider-local.ts replaces aiProvider.ts via next.config.ts webpack alias — all existing AI routes (chat, generate, summarize) work offline without any changes
- ·Settings page also shows the signed-in account and provides a Sign Out button wired to the Tauri sign_out_desktop command
- ·Account section, provider grid, model datalist with curated suggestions, write-only key input — all in one page at /dashboard/desktop/ai
Stream Facility Studio — Phase 4: Bidirectional Sync Engine
The full platform — worlds, codex, series, books, drafts, and scenes — now syncs between the Studio desktop app and your Stream Facility account. Pull your entire library down to SQLite for offline use. Push scene content back when you're ready. Everything is triggered manually from /dashboard/sync so you decide when data moves.
- ·Full library synced on pull: worlds, series, books, all codex entity types (characters, locations, items, creatures, organizations, concepts, lore, governments, magic systems, languages, religions, regions, planes), eras, world events, entity pages, and all story structure (drafts, acts, chapters, scenes)
- ·Pull is full-replace: local SQLite cleared and repopulated from the live platform in one operation — no conflict tracking needed for read-only codex data
- ·Push sends scene content only (last-write-wins) — draft structure is read-only offline to keep things simple
- ·An authenticated bulk sync endpoint returns the full library in one call; the desktop session token is sent in the Authorization header
- ·Sync state (lastPullAt, lastPushAt) stored in a local desktop_meta SQLite collection and displayed on the sync page
- ·Sync credentials and the platform URL are injected at launch — sync routes call the live platform directly without any browser involvement
Stream Facility Studio — Phase 3: Local Auth & License
Sign in once and stay signed in. Studio stores a 90-day session token in the OS keychain and verifies your license file on every launch — no internet connection required after the initial activation. Expired licenses still let you write forever; only sync is gated.
- ·Session JSON (userId, email, name, syncToken) stored in OS keychain via keyring crate — survives app restarts, OS updates, and reboots
- ·License file is a cryptographically signed payload — the verification secret is compiled into the binary, never readable from disk or environment variables at runtime
- ·Perpetual model: expired license shows a banner but the full writing experience remains unlocked indefinitely
- ·Activation flow: frameless 480×480 HTML window posts credentials to /api/desktop/activate, verifies the returned license, saves to keychain, spawns the Next.js sidecar, closes itself
- ·Sign out (IPC command sign_out_desktop): kills the sidecar, deletes session from keychain, deletes license from disk, closes the main window, reopens the activation form
- ·auth-local.ts reads session variables injected at launch — all dashboard auth checks pass without any cloud auth or database calls
- ·Long-lived session tokens are issued for the desktop app and used to authenticate sync calls to the live platform
Stream Facility Studio — Phase 2: Tauri v2 Shell
The desktop app is a thin Tauri v2 Rust wrapper around the full Stream Facility web platform, running locally. The Tauri binary spawns a Node.js sidecar, waits for it to start, then opens a WebView pointed at localhost. Everything in the web dashboard — worlds, codex, studio, beta — works offline from the same codebase.
- ·No bundled JS frontend: the WebView is http://localhost:{port}/dashboard — the actual Next.js app running on the machine
- ·Rust do_launch() picks a free port, resolves the sidecar path, injects the full env block, spawns Node, polls for TCP-ready, creates the main WebviewWindow, closes the splashscreen
- ·Splashscreen is a pure-CSS animated HTML file in desktop/dist/ — zero JS, zero bundler
- ·withGlobalTauri: true exposes window.__TAURI__.core.invoke in all windows; dangerousRemoteDomainIpcAccess allows http://localhost to call Tauri commands
- ·On main window close, Tauri kills and awaits the Node.js child process — no orphaned server processes
- ·find_server_js() resolution priority: SFSTUDIO_SERVER_PATH env var → production bundle in resource_dir → dev .next/standalone path
- ·desktop/ is a second Tauri app alongside the legacy desktop-legacy/ folder — both coexist in the repo
Stream Facility Studio — Phase 1: Next.js Sidecar Architecture
The desktop app now runs the full web platform locally instead of maintaining a separate native codebase. A Next.js standalone build becomes the sidecar — one build command produces both a Vercel deployment and a self-contained Node.js bundle the Tauri shell can spawn. Every dashboard page works offline without a line of duplicate code.
- ·DESKTOP_BUILD=true build flag switches Next.js to standalone output and activates four webpack server-bundle aliases
- ·mongodb.ts → mongodb-local.ts: SQLite-backed MongoDB Db surface using the node:sqlite built-in (Node 22.5+); documents stored as JSON blobs with indexed author_id and world_id columns
- ·auth.ts → auth-local.ts: reads session variables injected at launch by Tauri, returns a Pro-tier session; stubs signIn/signOut; middleware allows all requests unconditionally
- ·proxy.ts → auth-local.ts: same stub handles the NextAuth middleware handler import path
- ·aiProvider.ts → aiProvider-local.ts: reads AI config from variables injected at launch by Tauri rather than querying the cloud database
- ·No native compilation, no better-sqlite3, no Electron — node:sqlite is built into Node 22 and requires no install step
- ·Web platform on Vercel never loads any -local.ts file; aliases only activate when DESKTOP_BUILD=true is set at build time
Stream Facility Desktop — Phase 4H: Era Codex Sync, Timelines Nav & Draft Import
Fixes three issues discovered after first world import. Eras now appear in the Codex list — sync writes to both the eras table (for calendar use) and the entities table (for display). The sidebar has a dedicated Timelines section grouping Eras and Events. The Studio page gains an '↓ From Platform' button that fetches remote drafts and imports the full content (acts, chapters, scenes) into local SQLite in one step.
Stream Facility Desktop — Phase 4G: Full Codex Sync & Tabbed Entity View
The desktop now syncs the complete set of codex data from the platform — physical details, skills, aliases, connections, timeline entries, images, eras, and book genre — and displays it in the same tabbed layout authors see on the web (Profile, Physical, Skills, Aliases, Connections, Timeline, Extra). Previously only name, aliases, and a truncated summary were imported.
- ·New desktop-authenticated API routes for eras, connections, and timeline data
- ·Fixed root cause of era import failure: /api/eras uses session auth, so desktop token was silently rejected; new dedicated eras route fixes this
- ·Entities route expanded to return all character fields: role, bio, portraitUrl, heroImageUrl, age, lifeStatus, gender, race, eyeColor, hairColor, height, weight, build, distinguishingFeatures, appearance, skills, roles, traits, extra
- ·Alias bug fixed: MongoDB stores aliases as {name,...} objects; Rust now extracts just the name string, ending the [object Object] display issue
- ·Connections and timeline entries are bulk-fetched per world and embedded in each entity's fields JSON blob — no SQLite migration needed
- ·CodexDetail page rebuilt as a fully tabbed component matching the web platform; characters get Profile/Physical/Skills/Aliases/Connections/Timeline/Extra; other entity types get Details/Aliases/Extra
- ·Portrait and hero banner images display inline from Cloudinary/Vercel Blob URLs — no local download required
- ·Connections and Timeline tabs are read-only on desktop with a clear note directing authors to the platform for edits
Stream Facility Desktop — Phase 4F: Connect from Launcher
Authors who already have a platform world no longer need to create a blank local file first. A new 'or connect to a platform world →' path in the Launcher opens a self-contained flow: sign in, pick a world, preview the content, choose where to save the .sfstudio file, and import — all in one sequence.
- ·Five-state ConnectPlatformFlow component: checking → login → pick-world → confirm → saving; replaces the entire Launcher UI while active
- ·Reuses the existing preview_world_pull and pull_world_full Rust commands — no new backend code needed
- ·Confirm step shows the same series/books/entities/eras preview as WorldSyncPanel so authors know what they're importing
- ·File save dialog pre-fills the world name as the default filename (e.g. MyWorld.sfstudio)
- ·Retry Import button appears if the pull fails after the project file was created, so authors can recover without starting over
- ·No changes to WorldSyncPanel — both flows coexist; Launcher flow is for first-time import, WorldSyncPanel is for re-sync from WorldHome
Stream Facility Desktop — Phase 4E: Bidirectional World Sync
The desktop app now imports all content from a linked web platform world — series, books, codex entities, and eras — with a preview-then-confirm flow before any data is written. Both directions are manually triggered by the author. A WorldSyncPanel in the World Overview provides connection status, a world picker, a full import preview with counts, and an amber warning when local data already exists.
- ·Five-state panel: status → login → pick-world → confirm → done; preview step shows series names, book titles (capped at 4 + count), entity count, era count before any writes
- ·Amber warning block when local series/books/entities already exist — platform records update matching items, local-only items remain intact
- ·FK-safe upsert for series: INSERT OR IGNORE preserves existing local integer PK; UPDATE patches fields in place; SELECT builds remote→local ID map used when linking books
- ·New desktop-authenticated API routes for series and books metadata
- ·Three new Rust commands: get_remote_world_id (connection status), preview_world_pull (fetch without writing), pull_world_full (fetch + write + set sync_config.remote_world_id)
- ·WorldHome stats grid (Series, Books) reloads automatically after a successful sync via onSynced callback and syncKey increment
Stream Facility Desktop — Phase 3C: Full Codex, Series & Books
Codex entity editing expanded from pull-only to full local CRUD. Series and books are now managed locally in the desktop app with their own page. Migration 0003 adds the series and books tables and expands the entities table for local authoring. The activation flow was redesigned from importing a license file to a simple sign-in form.
- ·SQLite migration 0003: entities table gains dirty/description/fields/tags columns; new series table (title, genre, status); new books table with series_id FK (ON DELETE SET NULL); drafts gains local_book_id
- ·CodexList.tsx and CodexDetail.tsx pages — type-filtered list with inline search, full edit form with type-specific fields from entityTypes config, Saved ✓ confirmation, delete with confirm
- ·SeriesBooks.tsx — Series section with nested books, Standalone Books section; inline create/edit forms per item with genre and status dropdowns
- ·Activation redesign: LicenseGate replaced with sign-in form; activate_desktop Rust command fetches auth token + license in one step — no manual file download
- ·Launcher and LicenseGate both display current app version (vX.Y.Z) via @tauri-apps/api/app getVersion()
- ·Settings multi-provider AI: four tabs — Anthropic, Ollama (local, auto-detects models), OpenAI/compat (configurable base URL), OpenRouter (300+ models, searchable); active provider dot indicator; Check for Updates fetches GET /api/desktop/version from the platform
Stream Facility Desktop — Phase 4C: Era Date Anchoring
The desktop app now matches the web platform's in-world date anchoring system. Chapters can be pinned to an era and date range from the Plan view. Eras sync from the web platform alongside codex entities. A new SQLite migration adds era columns to acts, chapters, and scenes — tracked by a new schema_migrations table so ALTER TABLE never re-runs on an existing database.
- ·SQLite migration 0004: 6 era columns added to acts, chapters, and scenes (era_id, era_name, in_world_begins/ends_date and label); new local eras cache table
- ·Migration runner upgraded: schema_migrations table bootstraps itself first, then checks and records each version — ALTER TABLE ADD COLUMN is safe on existing databases
- ·pull_eras / list_eras Rust commands sync eras from /api/eras?worldId=..., stored locally; pull_eras called automatically alongside pull_entities in SyncPanel (best-effort)
- ·update_node_dates / clear_node_dates Rust commands dispatch to acts, chapters, or scenes by entity_type
- ·WorldDateInput and worldDate.ts ported from the web platform: zero Next.js imports, direct relative paths — the same calendar-aware date input used on the web
- ·InlineDatePanel in desktop PlanView: era picker from local eras table, WorldDateInput for begins/ends dates, calendar-aware labels via worldDateToLabel / realWorldDateLabel
- ·WorldDateBadge pill shows in_world_begins_date_label inline on chapter rows; ◷ clock icon toggles the panel
Studio: Chapter PATCH Bug Fix & Calendar-Aware Plan View Dates
The chapters PATCH route was only handling the summary field — chapter renames, order changes, and era date saves from the Plan view were silently dropped. Fixed to match the acts route pattern. The Plan view InlineDatePanel was also rebuilt to use the full calendar-aware WorldDateInput component, so custom calendar eras show their named units rather than generic year/month inputs.
- ·Chapter PATCH route now handles overrideName, order, eraId, eraName, inWorldBeginsDateLabel, inWorldEndsDateLabel, inWorldBeginsDate, and inWorldEndsDate
- ·parseWorldDate() helper validates that an incoming value uses only known WorldDate keys with numeric values before the field is accepted
- ·InlineDatePanel (web PlanView) rewritten to use WorldDateInput + buildDatePayload — same calendar-aware component used throughout the rest of the platform
- ·Era picker fetches full CalendarData from /api/eras; WorldDateInput receives the calendar and shows named units for custom calendars (e.g. Seasons, Moons)
- ·makeLabel() dispatches to worldDateToLabel for custom calendars and realWorldDateLabel for real-world mode
Studio Summarize — AI-Suggested Entity Page Pre-fill
Clicking '+ Create period page' on a SummarizePanel entity change card now carries the AI's suggested field values across to the EntityPagesTab form. Bio, role, life status, and appearance fields arrive pre-populated and highlighted in amber — the author can confirm them as-is or edit before saving.
- ·suggestedFields added to the EntityChange type returned by the summarize API: bio, role, lifeStatus, appearance, notes
- ·SummarizePanel onClick saves suggestedFields to sessionStorage under sf_ai_page_prefill_{entityId}; EntityPagesTab reads and clears the key on mount
- ·Pre-populated fields get an amber background highlight; an 'AI' badge appears beside the field label
- ·Highlights clear individually when the author edits a field; all clear when the period page is saved
- ·SessionStorage used instead of URL params (bio text too long) or React state (doesn't survive route navigation)
Studio Write View — AI Scene Summarize
A ∑ button in the Write view runs a one-shot AI analysis of the current scene, returning a 1–2 sentence summary and a list of entity state changes detected in the prose. On the web platform, it also checks whether each detected entity already has a period page covering the scene's in-world date — and surfaces a '+ Create period page' CTA when one is missing.
- ·Single non-streaming AI call returns structured JSON: { summary, entityChanges[] } — entity changes include detectedChange and suggestedNote fields for each mentioned entity
- ·Entity detection replicates the same word-boundary regex as the entity mention plugin against plain text — no TipTap JSON parsing required
- ·Web: resolveEntityState called per detected entity against the scene's in-world date; amber card highlight + '+ Create period page' CTA when hasExistingPage is false
- ·rightPanel state ('assist' | 'summarize' | null) replaces showAssist boolean — AI Assist and Summarize panels are mutually exclusive
- ·Desktop: summarize_scene Tauri command handles all three AI providers (Anthropic, OpenAI-compatible, Ollama); entity detection runs client-side from the loaded entitiesRef
- ·createChatCompletion added to aiProvider.ts for one-shot non-streaming calls alongside the existing streaming createChatStream
Stream Facility Desktop — Phase 4A: License & Distribution
The desktop app now ships with a full license system: a signed license file downloaded from your account page, verified cryptographically at startup, and stored in the OS app data directory. Missing license shows a gate screen; expired license shows a banner but writing keeps working forever.
- ·License file: a cryptographically signed payload containing account info, app version, and a 1-year validity window; verified at startup against a secret compiled into the binary
- ·The verification secret is embedded at compile time — never read from environment variables or disk at runtime; dev builds use a safe placeholder automatically
- ·Startup check: Missing → LicenseGate screen (Get License opens browser, Import License File opens file dialog); Expired → crimson banner, full writing still works, sync disabled; Active → normal app
- ·Web: GET /api/desktop/license generates and serves the signed file as a browser download; /dashboard/desktop page with OS download links and the Download License button
- ·Import flow verifies HMAC before writing the file — no restart needed, React state updates immediately after import
- ·Desktop nav link added to the inside nav bar
Stream Facility Desktop — Phase 4D: Full-text Search
Search across all scenes in a draft from the Plan view. A 320px search panel slides in from the right, debounces input at 300ms, and shows up to 50 hits with a context snippet and direct navigate-to-scene on click.
- ·SQL LIKE query on TipTap JSON content — no FTS5 migration needed; fast enough for typical draft sizes
- ·Rust recursively extracts plain text from TipTap JSON nodes and builds a ~160-char snippet centred on the match
- ·Results grouped and ordered by chapter → scene; each hit shows chapter name, scene name, snippet, and word count
- ·Search and Sync panels are mutually exclusive — opening one closes the other
- ·Click any result to navigate directly to that scene in Write view
Stream Facility Desktop — Phase 4B: Settings & Planning Notes
A dedicated Settings page consolidates account info, platform URL, AI key management, and Write view typography preferences. Separately, chapter summaries and scene notes — fields that existed in the schema but were never shown — now appear as inline text areas in the Plan view on both desktop and web.
- ·Settings page: Account (email + sign out), Platform (editable URL saved to preferences.json), AI Assist (key status, set/remove), Write View (font size and line spacing toggles)
- ·Font size (Small/Medium/Large) and line spacing (Normal/Relaxed/Loose) stored in preferences.json; WriteView reads on mount and applies as CSS custom properties --write-font-size and --write-line-height
- ·Gear ⚙ icon added to Studio and Plan view toolbars
- ·Chapter summary: two-row textarea below chapter header, save-on-blur
- ·Scene notes: single-row textarea below each scene row, save-on-blur
- ·Web platform: new PATCH /api/studio/[draftId]/chapters/[chapterId] route for chapter summaries; both views updated
Stream Facility Desktop — Phase 3B: AI Write Assist
A ✦ Assist panel slides in on the right side of the Write view. Four modes — Continue, Improve, Brainstorm, Custom — send selected text or the full scene to Claude and return a response. The Anthropic API key is stored securely in the OS keychain, never on disk.
- ·Four modes: Continue (1–3 paragraph continuation), Improve (rewrite for clarity and rhythm), Brainstorm (3–5 concrete next ideas), Custom (author-specified instruction)
- ·Selected text used as focus if present; full scene text always included as context — no character limit
- ·Anthropic API key stored in OS keychain via the keyring crate — same keychain service as the sync auth token
- ·Insert at cursor inserts the response directly into the focused TipTap editor at cursor position
- ·Copy button for using the response outside the app; ✕ to dismiss without inserting
- ·Remove API key option in the panel footer resets to the key-setup view
Stream Facility Desktop — Phase 3A: Entity Sync & Markdown Export
Codex entities from the web platform sync to the desktop app and appear as amber highlights in the Write view. Clicking a highlighted name shows a popup with the entity's type, name, and summary. Drafts can be exported to Markdown with a native save dialog.
- ·Entity sync: authenticated desktop endpoints list the author's worlds and return entities for all codex types
- ·Entities stored locally in a SQLite entities table — name, aliases (JSON), summary, entity_type, world_id
- ·Entity mention plugin ported from the web platform: ProseMirror decoration overlay highlights entity names in amber in Write view without changing stored TipTap JSON
- ·EntityPopup: click any highlighted entity name to see a fixed-position popup with type badge, name, and summary
- ·SyncPanel world picker: Sync Entities button opens a world list; selecting a world pulls entities into SQLite
- ·Markdown export: PlanView Export button opens a native save dialog; Rust tiptap_to_markdown() handles paragraph, heading, blockquote, lists, hr, and all inline marks
Stream Facility Desktop — Phase 2: Core Writing Loop
The full writing loop is working in the native desktop app: plan your draft structure, write in a continuous-scroll TipTap canvas, save version snapshots, and sync to the web platform. PlanView has full drag-and-drop reordering of acts, chapters, and scenes.
- ·PlanView: Acts → Chapters → Scenes hierarchy with @dnd-kit drag-and-drop, inline rename, collapse/expand, word count totals, Expand All / Collapse All
- ·WriteView: continuous-scroll TipTap canvas — all scenes render simultaneously; toolbar applies to the focused scene; Scene Break and Chapter Break insertable at cursor
- ·Scene version control: ⎘ Save Version and ⎇ Version History per scene; ⎘ Save Manuscript Version for a bulk snapshot across all scenes
- ·Sync panel: sign in with Stream Facility credentials, link local draft to a web draft, push dirty scenes, pull updates
- ·Authenticated desktop API routes on the web platform: session token issuance, draft listing, and scene sync endpoints
- ·Entity mention plugin registered from launch with a live entity list — highlights update after every entity sync
Stream Facility Desktop — Phase 1: Foundation
A native desktop writing app built with Tauri v2 — roughly 5 MB vs Electron's 150 MB. A .sfstudio SQLite file is created or opened at launch. The Launcher screen handles New World, Open World, and a Recent Worlds list. All draft, chapter, scene, revision, and sync tables initialise on first open.
- ·Tauri v2 + React + Vite + TypeScript — one binary, no Electron, no Chromium bundle
- ·SQLite schema with WAL mode: drafts, acts, chapters, scenes, scene_revisions, sync_config — mirrors web platform MongoDB collections
- ·Project stored as a portable .sfstudio file (SQLite database) — copy the file to move the project
- ·Launcher: New World / Open World dialogs via Tauri file dialog; Recent Worlds list in the sidebar
- ·Full type-safe Rust command layer in src-tauri/src/commands/ with tauri-commands.ts wrapper on the frontend
- ·Same sf-* design tokens as the web platform via a copied @theme {} block — zero UI divergence
Ad System — Tier Gating & Subscription Visibility
Free-tier users see ads in the Lobby and Library; paid tiers see none. Ads are fully configurable from the platform dashboard — paste any provider's embed code and it goes live immediately, or leave blank for house ads promoting the Indie plan. Purchasing a paid subscription hides ads and the active plan is now clearly shown on the user's profile.
- ·AdZone component: house ad (Indie upgrade CTA) with fallback; script tags injected via DOM creation so third-party scripts execute correctly in both RSC and client components
- ·Lobby: two ad placements (after Authors, between Coming Soon and New Releases); Library refactored to server wrapper + LibraryClient so showAds resolves server-side
- ·Ad placement management: paste any provider embed code (AdSense, Carbon, custom); status indicator (house ads vs. custom active); provider quick-reference guide; clear button
- ·Config API patched to merge per top-level key so Tier Config and Ads settings save independently without overwriting each other
- ·Stripe webhook fully wired: checkout.session.completed sets tier on purchase, customer.subscription.deleted reverts to free, invoice.payment_failed marks past_due
- ·Profile page: new Plan & Subscription section — tier badge, benefit summary, Manage Billing button (Stripe portal) for paid users, past-due warning with payment update CTA
- ·Subscriber dashboard: tier cell shows Stripe/manual source and subscription status for all paid-tier users
Codex Tag Expansion & Entity Page Skills
All major codex entity types now carry predefined tag fields — roles and traits for characters, faction/location/item/creature/organization tags, and series themes. Entity pages gain a skills snapshot field so skills active during a specific period override the base character profile in AI context.
- ·Predefined vocabulary: CHARACTER_ROLES, CHARACTER_TRAITS, CHARACTER_SKILLS, LOCATION_TAGS, FACTION_TAGS, CREATURE_TAGS, ITEM_TAGS, ORGANIZATION_TAGS, SERIES_THEMES in src/lib/codexTags.ts
- ·TagInput component extended with optional suggestions prop — clickable chips for one-click add
- ·Characters Skills tab: roles and traits TagInputs with predefined archetypes and personality tags
- ·Entity page skills snapshot: skills active during a specific in-world period; resolveEntityState merges page skills over base entity skills
- ·AI context: formatEntity() now emits Roles: and Tags: lines alongside existing Skills: and Traits:
Entity Pages — Temporal Connections
Entity pages now support page-to-page connections across all codex entity types. Each connection links a source page to the specific page of the target entity that overlaps the same in-world time period — so Amelia's Oct 2024–Dec 2025 page links to Iron Guild's page for the same era, not just the entity itself.
- ·45-label controlled vocabulary grouped by category (Affiliation, Opposition, Alliance, Social, Location-based, Possession, Event-based, Succession, Knowledge, Belief, General)
- ·Temporal overlap resolution: auto-finds and suggests the target entity's overlapping page; no-end-date pages treated as ongoing
- ·Bidirectional connections: author-controlled toggle writes a reverse connection to the target page with linked IDs for precise cleanup
- ·UI: entity type picker → debounced codex search → auto-resolved page → label dropdown → bidirectional toggle, all inline on each PageCard
- ·AI Write Assist: connections injected into context with 1-level-deep page fetch (role + bio from linked page)
Beta Reader Manuscript Watermarking
Invisible watermarks are now embedded in every scene shown to beta readers. If a manuscript excerpt surfaces outside the platform, the watermark identifies both the original author and the specific reader who leaked it.
- ·Two complementary layers applied simultaneously — one detectable by AI text extraction, one by AI vision analysis of screen captures
- ·Watermarks encode both author name and reader identity
- ·Fully invisible to humans at all reading distances and in all rendered contexts
- ·Applied only to beta readers — general public readers see nothing
- ·A decode utility can forensically recover the full payload from any extracted copy
Beta Reader Comment Threads
Beta readers and authors can now have full conversation threads under each scene of a manuscript. Readers in the same campaign see each other's comments. The author can reply from the Write view without leaving the editor. Every comment records which scene revision it was made on.
- ·Comment threads appear below scene prose in the ReadingTree (reader view) and in an inline panel inside the Write view (author view)
- ·Author comments appear with a gold Author badge; any participant can reply to any top-level comment
- ·Same-campaign scoping: readers see only their own campaign's thread plus general author notes
- ·Author replies are automatically visible in the correct campaign thread without any extra configuration
- ·Each comment records the scene revision it was made on — visible as a small badge on the comment
- ·Cmd/Ctrl+Enter submits from any textarea; no page reload required
- ·Author notified in-app whenever a beta reader posts a comment
Security: Access Control & Gate Improvements
Two access control issues corrected following the unified profile rollout. The Forge was incorrectly gating on a role that no longer exists, blocking authenticated users from reaching their dashboard. Separately, a tier assignment edge case was resolved to prevent unintended tier changes during account management operations.
- ·The Forge now correctly allows any authenticated user to reach their dashboard; tier limits apply inside
- ·Tier assignment logic tightened to prevent unintended tier changes during account role updates
Studio: Scene Version Control
Authors can snapshot any scene, chapter, or entire manuscript with a single click. Revisions are stored per-scene and can be browsed and restored without leaving the Write view. Beta readers see badges showing which scenes are new or have been revised since their last read. Sidebar version buttons keep controls in view while scrolling through long manuscripts.
- ·scene_revisions collection stores per-scene snapshots: content, wordCount, optional label, createdAt — bulk saves share the same timestamp for easy grouping
- ·Per-scene ⎘ (Save Version) and ⎇ (Version History) in the scene header; history panel rendered above the editor so it is immediately visible without scrolling past prose
- ·Word count and version buttons styled in gold (text-sf-accent / text-sf-accent/60) to match chapter and scene headings
- ·Manuscript-level bulk checkpoint: '⎘ Save Manuscript Version' toolbar button; saves all scenes in a single insertMany; inline label input + '✓ N scenes saved' confirmation
- ·Chapter-level checkpoint via POST /api/studio/[draftId]/checkpoint with optional chapterId — same route, scoped to one chapter
- ·Left sidebar widened from w-64 to w-80; ⎘ per chapter, ⎇ + ⎘ per scene, always visible while scrolling; ⎇ scrolls to the scene and opens its history panel
- ·Sidebar history opener uses useRef<Map<string, () => void>> registration via an always-updating ref pattern to avoid stale closures across multiple concurrent TipTap instances
- ·Beta reader badges: scene_revision_reads collection tracks when each reader opens a scene; green Revised badge if revised since last read, purple New if never opened
- ·Plan view amber dot on scenes with beta comments newer than the latest revision
Unified Author/Reader Profile Model
Eliminated the author/reader registration toggle in favour of a single unified account. Author status is earned (and sticky) the moment a draft is made available or a book is set public. Free-tier users can access the Forge with configurable limits; all tier limits are configurable via the management dashboard.
- ·isAuthor flag earned when draft accessMode→free/paid or book isPublic→true; never revoked
- ·isAuthor carried in JWT and refreshed every 5 minutes from DB
- ·Free tier gets Forge access: 1 world, 1 book, 0 series, 25 codex entries, 0 eras, 1 draft
- ·TierConfig expanded with maxSeries, maxCodexEntries, maxEras, maxDrafts, canAccessForge
- ·All 5 new limits are configurable in the platform management dashboard
- ·InsideNav unified — all users see the same nav including Forge and Studio links
- ·Discovery queries (lobby, authors, search) filter by isAuthor:true instead of role
- ·Registration page simplified to name/email/password — no role selection
- ·Public /readers/[id] profile works for all users; label shows Author or Member dynamically
Beta Reads in The Hearth
Approved beta readers now see their active manuscripts directly in the Library (The Hearth). A new Beta Reads section surfaces above the reading list tabs, showing each campaign with a deadline countdown, state-aware CTAs, and a review prompt when reading is complete.
- ·New GET /api/beta-reads returns approved, completed, and expired applications with campaign title, author name, and review status
- ·Crimson rule section header matches the wing personality of The Stage
- ·Left border stripe encodes state: crimson (active), muted gold (completed), grey (expired)
- ·Deadline countdown turns crimson at ≤3 days remaining
- ·Active reads show Read → link to /beta/{id}/read
- ·Completed reads prompt Leave review → if not yet reviewed, or show Reviewed ✓
- ·Section invisible to users with no beta history — no empty state
Beta Campaigns: Launch from Studio
Authors can now initiate or manage a beta reading campaign directly from the Studio manuscript toolbar. The Beta button sits alongside Plan, Write, Review, Chat, and Export — and knows whether a campaign already exists for this book.
- ·No active campaign: '+ New Campaign' button (crimson-tinted) appears in the Studio toolbar
- ·Active campaign: 'Beta ↗' (subtle bordered) links to the campaign manage page
- ·Studio layout fetches bookId, betaId, worldName, and seriesName from the draft record
- ·New campaign URL encodes ?bookId, ?world, ?series — context appears as a line under the form header
- ·New campaign form auto-selects the matching Studio draft and skips to step 2 with title and book pre-filled
- ·Beta campaign manage page shows '← Manuscript' breadcrumb link back to the Studio plan view
Lobby: Wing Directory
The lobby page now has a four-panel wing directory between the masthead and the following feed. Each panel is a clickable entryway to one of the platform's four areas — The Hearth (Library), Local & You (Authors), The Stage (Beta), and The Forge (Dashboard/Studio).
- ·gap-px bg-sf-border grid — the border colour is the gap; panels sit flush against each other
- ·Short h-px w-8 rule at the top of each panel encodes wing personality in colour
- ·The Hearth (gold): /library if signed in, /sign-in if not
- ·Local & You (dim gold): /authors — community discovery
- ·The Stage (crimson): /beta — open manuscripts for beta reading
- ·The Forge (subtle or border): /dashboard if author, /pricing if not
- ·sm:grid-cols-2 lg:grid-cols-4 — 2×2 on tablet, 4-wide on desktop
- ·Compass direction labels removed — those were internal book references, not UI copy
Slug URL System
All public and dashboard URLs now use human-readable slugs instead of raw MongoDB ObjectIds. Slugs are generated at creation time and lazily migrated on first visit for existing entities. The server wrapper pattern keeps client components decoupled from slug resolution.
- ·slugify() + resolveEntitySlug() in src/lib/slugUtils.ts handle creation and resolution
- ·Lazy migration: first ObjectId visit generates and saves the slug, then redirects
- ·Server wrapper pattern: thin page.tsx resolves slug → ObjectId; PageClient.tsx receives OID
- ·Dashboard pages updated: books, series, worlds, codex pages, timeline, currently-writing widget, global codex, persona links
- ·MongoDB projection caveat: restricted projections must explicitly include slug: 1
- ·Edit subroutes (/edit) use useParams() and continue to accept ObjectIds — slug support deferred
Email System: Verification, Daily Digest & Author Mailing Lists
The platform now has a complete email system built on Resend. New accounts must verify their email before accessing the dashboard. Authors get a daily digest of unread notifications instead of individual emails. Authors can collect subscribers via an external mailing list link or the built-in subscriber system with their own Resend key.
- ·Email verification: new accounts created with emailVerified=false; 24-hour token sent on registration; middleware blocks /dashboard and /studio until verified
- ·Daily notification digest cron at 08:00 UTC: groups unread notifications per author into one email; digestSentAt field prevents double-sending; per-author opt-out toggle in Profile
- ·Author mailing list — external path: paste a Mailchimp/ConvertKit/etc URL in Profile; 'Join my mailing list' button appears on public profile
- ·Author mailing list — platform path: store own Resend API key (encrypted at rest) + verified from-address; native subscribe widget on public profile
- ·Subscriber endpoint is rate-limited; unsubscribe via tokenized one-click link appended to every campaign email
- ·Campaign sending batches recipients at a time using the author's own Resend key (author owns cost + CAN-SPAM compliance)
Security Hardening: Pre-Production Audit
Full security audit completed before moving to a live server. All HIGH and MEDIUM severity issues resolved. Covers authentication, authorization, injection prevention, rate limiting, content security headers, account data isolation, and ESLint security rules.
- ·Session security: short-lived tokens with periodic live re-validation; automatic sign-out on account suspension, deletion, or credential change
- ·Rate limiting on registration, login, password reset, email verification, and subscriber signup
- ·Security headers: CSP, HSTS with long-term preload, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy
- ·Central middleware enforces authentication on all protected routes
- ·Strict ownership verification on all resource-scoped API endpoints
- ·Input validation and allowlist enforcement on all data-modification and AI endpoints
- ·Secure file delivery for manuscript downloads; file type validation on all uploads
- ·API responses use explicit field allowlists — sensitive account data is never returned to the client
- ·Account deletion covers all user data across the platform; internal debug utilities removed from production
- ·eslint-plugin-security rules enforced as errors across the codebase
WorldMapView: Interactive World Map with Era-Driven Time Player
The world detail page now shows an interactive map with entity pins and an era-anchored time player. Scrub through the world's timeline and watch pins appear and disappear as entities enter and leave each region. The time axis uses the same era coordinate system as the visual Gantt timeline.
- ·Era-anchored slider: min/max = first era startSortKey → last era endSortKey — same axis as the visual Gantt timeline
- ·Segmented era track with proportional color bands, active era at full opacity, red scrubber line
- ·Pins visible when their time range covers the current scrubber position (null bound = always visible on that side)
- ·Ghosted pins: hidden (time-filtered) pins rendered at 25% opacity so the author always sees all placements
- ·Click any pin to open a selected-pin card with entity name, type badge, label, and date range
- ·Current position shown as 'Era Name · specific date label'
- ·Returns null (no-op) when the world has no mapImageUrl — no empty state to manage
World Map Placements System
Authors can pin any codex entity onto the world map at one or more points in time. A MapTab component on entity detail pages provides a click-to-place map picker with mini-map thumbnail cards. Positions are stored as percentages so they work at any image resolution.
- ·map_placements collection: mapX/mapY as 0–100% of image dimensions; entityName denormalized for join-free world queries
- ·beginsSortKey / endsSortKey use worldDateToSortKey() — same formula as entity_pages for range queries
- ·GET /api/map-placements?worldId= returns all pins for WorldMapView; ?entityId+entityType= for MapTab
- ·POST auto-fetches entityName from the source collection across all 21 entity types via COLLECTION_MAP + NAME_FIELD tables
- ·MapTab: click-to-place picker with crosshair overlay, mini-map thumbnail cards, EraDateSection for time range
- ·MapTab now wired to Characters, Locations, Regions, Factions, Creatures, Items, and Organizations
- ·linkedEventIds checkbox picker in the placement form: link any placement to world events; count shown on thumbnail cards
- ·mapImageUrl field added to Location and Region APIs and detail pages (ImageUpload component)
Studio Write: Codex Entity Highlighting
Entity names from the codex are now highlighted in amber in the Write view as you type. Hovering any highlighted name shows a popup with the entity's current world-state — era-aware bio, role, status, and portrait. Persona aliases map to their parent character's card.
- ·ProseMirror decoration plugin — no changes to doc structure, pure visual overlay
- ·An authenticated endpoint fetches all codex entities for the draft's linked world
- ·Longer names matched first so 'Amelia Williams' takes precedence over 'Amelia'
- ·Persona aliases highlighted but popup shows the canonical parent character's codex card
- ·Hover popup fetches the entity's world-state using the scene's era context for era-accurate display
- ·Popup shows portrait/image, type badge, role (accent), bio (3-line clamp), and link to codex
- ·160ms hover delay prevents flicker; popup hover cancels the close timer
Studio: Write Tab, Scene Persistence & Navigation Fixes
A Write tab now appears in the Studio toolbar alongside Plan, Review, Chat, and Export. The last scene visited in the Write view is persisted to localStorage so clicking Write returns you to exactly where you left off. Two bugs fixed: the Review page crashed with a MongoDB projection error, and the /write fallback redirect returned 404.
- ·Write tab added to StudioShell toolbar; links directly to the last-visited scene
- ·Last scene stored in localStorage under studio-last-scene-{draftId}
- ·Fallback /write route (server component) redirects to the first scene when no stored scene exists
- ·Scroll-to-scene on Write load deferred 300ms so TipTap editors render at full height first
- ·Review fix: invalid MongoDB mixed inclusion/exclusion projection replaced with conditional pure-inclusion
- ·Redirect fix: scene ownership verified via the draft document (scenes don't store authorId)
Personas: First Appears In Fix & Aliases Dirty State
Two character codex improvements: the persona creation form now correctly captures and persists the 'First Appears In' book and chapter (a bug that silently dropped the value), and the Aliases tab Save button is gated on a dirty state so it's only active after a change is made.
- ·Persona creation form adds dedicated 'First appears in book / Chapter' fields above the reveal section
- ·introducedIn and introducedInChapter sent to POST /api/character-personas and persisted on the persona doc
- ·Save Name Variants button disabled until a name variant is actually changed
- ·'Name variants saved.' confirmation shown for 3 seconds after a successful save
- ·aliasesDirty state set on any AliasInput onChange; cleared automatically on save
World State Resolver
A new GET /api/world-state endpoint takes an entity, an era, and an in-world date and returns the entity's state at that exact moment — base profile merged with the active Page snapshot. This is the core operation that AI chat and writing assist will use to answer 'what was X like in Year 847?'
- ·Accepts entityId, entityType, eraId, and sortKey (integer from worldDateToSortKey)
- ·Finds the Page snapshot whose period covers the requested date (inWorldBeginsSortKey ≤ sortKey ≤ inWorldEndsSortKey)
- ·Merges base entity fields with Page snapshot fields — Page fields win when non-empty
- ·Returns activePage metadata alongside the merged state for transparency
- ·Supports all six entity types: character, item, faction, location, creature, organization
Entity Pages: Date Sort Keys
Every Page now stores inWorldBeginsSortKey and inWorldEndsSortKey — integer values derived from the WorldDate object at save time. These enable reliable range queries for the state resolver and future AI chat system without relying on string comparison.
- ·worldDateToSortKey() added to src/lib/worldDate.ts
- ·Formula: effectiveYear × 100,000,000 + month × 1,000,000 + week × 10,000 + day × 100 + hour
- ·Coarser fields (millennium, century, decade) treated as year offsets for monotonic ordering
- ·String-format dates (free-text fallback) produce null sort key — they cannot be range-queried
- ·Computed on POST and recalculated on PUT in entity-pages routes
Codex: Persona Alias System
Aliases that represent a full alternate identity — 'Annabelle Westford' as a secret persona of 'Amelia Williams' — are now first-class character documents linked to their canonical character via a parent-child relationship. The connection stays hidden until the author-set reveal point, preventing spoilers from appearing on the codex page.
- ·Persona characters stored in the characters collection with isPersona: true + parentCharacterId
- ·Reveal gating: revealAtBook (ObjectId) + revealAtChapter (number) on the persona document
- ·Characters list and codex search both filter isPersona: { $ne: true } — personas never appear in listings
- ·Canonical character page shows a 'Persona Aliases' section in the Aliases tab
- ·Persona pages show an amber banner and an 'Identity Reveal' section with the gate settings
- ·DELETE on a canonical character cascades to delete all its persona aliases
- ·API route: GET/POST /api/character-personas
Codex: Entity Pages (Period Snapshots)
A new 'Pages' tab on character codex entries lets authors record period snapshots — what a character looked like, their role, and their status during a specific span of in-world time. Each page has two time axes: an in-world date range (for AI chat queries) and a narrative gate (for writing assist spoiler gating).
- ·Stored in entity_pages collection with entityId, entityType, eraId, inWorldBeginsDate/Label, inWorldEndsDate/Label, narrativeBeginsBook/Chapter
- ·Snapshot fields: bio, role, lifeStatus, hairColor, eyeColor, build, appearance, distinguishingFeatures, notes
- ·EntityPagesTab component handles full CRUD with collapsible physical fields
- ·Two time axes: in-world WorldDate range (AI chat) + narrative book/chapter gate (writing assist)
- ·API routes: GET/POST /api/entity-pages, GET/PUT/DELETE /api/entity-pages/[id]
Studio: In-World Date Anchoring
Acts and chapters in the Studio Plan view can now be anchored to an in-world era and date range. A clock icon on each row opens an inline date panel with an era selector and begins/ends text fields. The write page sidebar shows the active chapter's era and date below the chapter name.
- ·Click the ◷ icon on any act or chapter row to set its in-world period
- ·Era dropdown pulls from the world linked to the draft's book
- ·Begins and ends date labels stored on story_acts and story_chapters documents
- ·Date badge appears on the row when set; accent-colored ◷ icon stays visible as an indicator
- ·Write page sidebar shows era · begins date below each chapter name
- ·Creates a unified coordinate system: every scene position maps to both a narrative coordinate and a world date
Codex: Type Reassignment
Authors can move any codex entity from one type to another — a Character to a Creature, a Location to a Region, etc. A pre-confirmation panel shows exactly which type-specific fields actually have data and won't carry over, so nothing is lost by surprise.
- ·Moves the entity between MongoDB collections and redirects to the new page
- ·Common fields always carried over: name, description, aliases, world, era, images, Extra Notes
- ·Shows only fields with real data — no phantom warnings for empty fields
- ·Confirm checkbox required before the move is allowed
- ·Available on every codex entity detail page via '⇄ Change type…'
Codex Import: Duplicate Detection & Resolution
The import preview page now detects entries that already exist in the selected world and lets authors choose what to do with each one — insert a second copy, merge into the existing record, or rename before inserting.
- ·Duplicate check runs automatically when a world is selected (POST /api/import/check-duplicates)
- ·Flagged entries show an amber ⚠ duplicate badge
- ·Three per-entry actions: Insert new, Merge into existing (fills empty fields only), Rename & insert
- ·Rename shows a text input pre-filled with 'Name (imported)'
- ·Re-check button for when entry types are changed after the initial check
- ·Result screen shows inserted and merged counts separately
Codex: Third-Party Importer
Upload a writing app's .zip export and import any combination of its world-building entities — characters, lore, locations, factions, and more — directly into the codex. A preview step lets authors include or exclude specific items before committing.
- ·Supports all 19 codex entity types
- ·Green ✓ / red ✕ toggle pair on every entry row — defaults to included
- ·Unmapped fields captured in Extra Notes on every imported document
- ·Imported entities show a review banner until first saved
- ·importedAt timestamp set on import; cleared on first save
- ·Thumbnail images extracted from the ZIP and uploaded to Cloudinary — stored as imageUrl on the inserted document; non-fatal if missing
Codex: 5 New Entity Types
Five genre-agnostic entity types added to the world-building codex — Conditions, Knowledge, Practices, Resources, and Works — bringing the total to 19. Each is fully wired into the tab view, search, import, type reassignment, and codex-search.
- ·Conditions: status effects, ailments, curses (effects, cause, duration, cure)
- ·Knowledge: lore entries, legends, discovered truths (reliability, source, body)
- ·Practices: rituals, professions, martial arts, methods (practitioners, steps)
- ·Resources: materials, currencies, commodities (uses, rarity, sources)
- ·Works: in-world books, artworks (creator, status, content/excerpts)
- ·Knowledge and Works use 'title' as primary name field (same pattern as Lore)
- ·codex-search architectural fix: dynamic nameField lookup corrects lore/knowledge/works search
Image Upload Optimization
Two complementary layers keep uploaded images compact. Client-side canvas resizing fires before the upload even starts. Cloudinary URL transforms ensure optimized delivery to readers regardless of what was stored.
- ·Client-side: landscape capped at 2400 px, portrait at 1200 px, files over 2 MB compressed
- ·Canvas → JPEG 0.88 quality output; GIFs pass through unchanged (preserves animation)
- ·Spinner visible during resize phase, not just during the HTTP upload
- ·Cloudinary delivery transform injected into stored URL: c_limit, w_2000, q_auto, f_auto
- ·f_auto serves WebP or AVIF to browsers that support it
Save Confirmation Flash
All 23 codex entity and content detail pages now show a green 'Saved ✓' flash for 2 seconds after a successful save — eliminating silent saves that looked like nothing happened.
- ·SaveButton component manages three states: Saving… / Saved ✓ / Save Changes
- ·Green background on success state for clear visual confirmation
- ·Button disabled during both saving and the 2-second success window
- ·Applied across all codex entity, book, series, excerpt, and era detail pages
Codex: Extra Notes & Import Metadata
Every codex entity now has an Extra Notes field for imported metadata and freeform author notes. Imported entities display a warning banner prompting review. Saving any entity clears the import flag.
- ·Extra Notes textarea on all 14 codex entity detail pages
- ·Visible on imported entities or when the field already has content
- ·importedAt set to null on every PUT save — banner shows once after import
- ·Saves with the main form — no separate submission needed
Codex: Navigation & Delete Consistency
All 14 codex entity detail pages now navigate back to the correct type-filtered codex list after delete. Breadcrumbs consistently show 'Codex' as the parent link rather than world or book context.
- ·Delete redirects to /dashboard/codex?type=<key> on all entity pages
- ·Breadcrumb: Codex › Entity Name with link to the type-filtered list
- ·Consistent across all 14 entity types
Continuous Scroll Write View
All scenes render simultaneously in one scrollable canvas, each with its own editor. No more clicking to load the next scene — scroll through the entire draft, click any scene in the sidebar to jump directly to it.
- ·Every scene has an independent TipTap editor instance
- ·Toolbar applies to whichever scene is focused
- ·Sidebar scene links scroll and auto-focus the target scene
- ·Scene Break and Chapter Break work on the active scene at cursor position
Plan View: Expand/Collapse All & Inline Scene Preview
Expand or collapse the entire draft outline in one click. Reveal any scene's prose directly in the Plan view without navigating to Write — useful for reviewing structure and content together.
- ·Expand All / Collapse All buttons in the Plan view header
- ·Per-scene toggle reveals inline prose preview below the scene row
- ·Content lazy-fetched on first expand and cached for repeated toggles
- ·Individual node toggles continue to work independently
Draft Access Mode & Status
Authors control who can read their manuscript via an access mode toggle on the draft. A status badge marks drafts as in-progress or finished.
- ·Private: manuscript hidden on the public book page (default)
- ·Free: full prose visible to any visitor
- ·Paid: chapter/scene structure visible; scene content gated
- ·Status badge (Draft / Finished) shown in Plan view
ReadingTree on Public Book Page
The public book detail page embeds the manuscript as an expandable act → chapter → scene tree. Approved beta readers get full prose access regardless of the draft's public access mode.
- ·TipTap JSON rendered inline — no editor overhead
- ·Reading progress checkboxes per scene, stored per user
- ·Expand All / Collapse All for the full tree
- ·Beta reader access granted via approved application with active deadline
- ·Multi-campaign support: access granted if approved under any campaign for the book
Beta Campaigns: Studio Draft Support
Beta campaigns can now use a Studio draft as the manuscript source — not just uploaded files. Selecting a Studio draft in the campaign wizard auto-fills the linked book and target audience.
- ·Campaign creation lists Studio drafts alongside uploaded manuscripts
- ·Selecting a Studio draft auto-populates Linked Book and Target Audience
- ·Approved readers land on the public book page (ReadingTree) instead of the legacy file reader
- ·manuscriptSource field distinguishes studio vs. upload campaigns
Beta Reader: Real-time Deadline Enforcement & Reapproval
Access is checked against the live deadline at every entry point — no gap between expiry and the nightly cron job. Authors can reapprove expired readers to grant a fresh deadline.
- ·All read-access checks gate on deadlineAt > now in real time
- ·Campaign detail page shows expired status immediately when deadline passes
- ·Days remaining counter on the reader's campaign page (amber < 7 days, red < 3 days)
- ·Reapprove button in Dashboard → Beta → Applications for expired readers
- ·Reapproval sends a fresh approval email with a new deadline
Beta Reader: Inline Scene Comments & Threads
Beta readers and authors hold full threaded conversations under each scene of a manuscript. The author replies from the Write view. Comment threads are campaign-scoped so readers see only their own campaign's discussion.
- ·Comment threads appear below scene prose in ReadingTree (reader) and as an inline panel in the Write view (author)
- ·Author badge in gold; any participant can reply to any top-level comment (one level deep)
- ·Same-campaign scoping — reader sees their campaign thread plus general author notes
- ·Each comment records which scene revision it was made against
- ·Author notified in-app when a reader posts; readers notified when author replies (fan-out via insertMany)
- ·Cmd/Ctrl+Enter submits; no page reload
- ·Plan view amber dot on scenes with unaddressed comments newer than the latest revision
Studio — Story Creator (Phases 1–4)
Full writing suite at /dashboard/studio. Four views per draft: Plan (structural outline), Write (TipTap editor), Review (word counts + codex heatmap), Chat (AI conversation). Available to Indie and Pro tiers.
- ·Acts → Chapters → Scenes hierarchy with drag-and-drop reorder
- ·Inline Scene Break and Chapter Break in the Write view
- ·Codex entity heatmap in Review (mentions per scene across all 14 entity types)
- ·AI Chat with +Context picker (Pro only)
- ·DOCX / Markdown / Plain Text export with scope selector
Studio — AI Write Assist & Prompt Library
A '✨ Write' panel in the Write view. Choose a prompt template, fill in variables, and the AI streams prose into a preview. Insert at cursor, regenerate, or fork the prompt into a personal library.
- ·6 built-in prompts: Draft from Beat, Continue Scene, First Draft, Expand, Dialogue, Rewrite
- ·Auto-injected context: draft title, chapter label, current scene content
- ·User prompt library with fork-from-system support
- ·Pro only; requires ANTHROPIC_API_KEY
Studio — Manuscript Import (DOCX / PDF)
Upload a DOCX or PDF to the Studio draft list and have it land as a fully structured draft — acts, chapters, and scenes auto-detected from the heading hierarchy.
- ·DOCX headings (H1/H2/H3) map to acts, chapters, and scenes
- ·Inline marks preserved: bold, italic, underline, strikethrough
- ·PDF imported as a single scene; author splits manually with Break tools
- ·Drag-and-drop import dialog with success screen and stats
Beta Reader System
Authors run beta reading campaigns for manuscripts. Readers discover campaigns, apply, read, and submit structured reviews. Deadline tracking and nightly expiry cron included.
- ·Campaign discovery at /beta with genre filter and pagination
- ·Structured review form: per-category star ratings, comments, strengths/weaknesses
- ·Nightly cron auto-expires past-deadline applications and sends warnings
- ·Campaign defaults configurable: deadline days, warning window, extension limit
- ·Manuscript revision history with reader notification on new upload
Character Expansion: Physical, Aliases & Connections
Characters gained a four-tab detail page (Profile, Physical, Aliases, Connections), reveal-gated aliases for AI writing assist, and links to any codex entity.
- ·Physical fields: race/species, eye color, hair, height, weight, build
- ·Aliases with revealedIn book + chapter — AI context gating
- ·Connections tab: links to characters, locations, factions, events, items, vehicles
- ·New entity types: Items/Artifacts and Vehicles
World-Building Codex: 7 New Entity Types
Seven additional entity types added to the codex: Eras, Languages, Magic & Power Systems, Religions & Deities, Regions, Governments, and Organizations. All share the same author + world scoped pattern.
- ·Eras with structured WorldDate start/end and visual timeline integration
- ·Magic & Power Systems: source, cost/toll, limitations, rules
- ·Religions: type dropdown (Deity, Pantheon, Religion, Cult…)
- ·Codex nav dropdown in dashboard nav covers all 14 entity types
World Timeline Events & Visual Gantt
A visual Gantt chart for world timelines with proportional era columns, fractional year event positioning, zoom, pan, and bidirectional entity links.
- ·Era columns sized proportionally by year span
- ·Fractional year positioning: 15 March 2023 → 2023.18
- ·Adaptive tick marks: years, months, or days depending on zoom level
- ·Mouse wheel zoom and click-drag pan
- ·World events linked to any codex entity appear on that entity's Timeline tab
Cross-Entity Search
Search across Books, Series, Authors, Worlds, and Beta campaigns from a single search bar. Genre, audience, and rating filters. Sortable results per tab.
- ·Lobby masthead search bar navigates to /search?q=...
- ·Five tabs: Books, Series, Authors, Worlds, Beta, All
- ·Genre filter, audience filter, min-rating filter (Books only)
- ·URL-driven filters — shareable and bookmark-able
- ·Genre tag chips on all public pages link directly to filtered search
Reviews & Ratings
Readers rate and review any public book. One review per reader per book. Ratings surface on author profiles and search results.
- ·Star picker (1–5) with optional title and body
- ·Average rating and count shown on public book and author pages
- ·Edit and two-step delete for own reviews
- ·Authors cannot review their own books
- ·Recent Reviews section on public author profiles
Reader Profiles & Personal Library
Readers have public profiles showing reading stats, favorite genres, reviews, and an 'open to beta reading' badge. A personal library tracks Want to Read / Currently Reading / Finished.
- ·Public profile at /readers/[id] with hero banner
- ·Beta reads completed stat, open-to-beta badge
- ·Want to Read shelf (private), reading list (public)
- ·Favorite genres tag-picker on dashboard profile page
Social — Follows & Following Feed
Authors and readers can follow each other. The Lobby shows a Following feed with recent books, beta campaigns, and worlds from followed authors.
- ·Follow / Unfollow button on author and reader profiles
- ·Follower and following counts on profiles
- ·Following feed in Lobby: new books, open beta campaigns, new worlds (last 60 days)
Platform Shop
Three product types: subscription gift codes, digital content, and physical products. Gift codes redeemable at /redeem extend or grant a subscription tier.
- ·Gift certificates generate a one-time code after Stripe checkout
- ·Gifted tier stacks with paid tier; same-tier codes extend expiry
- ·Products linked to Stripe one-time price IDs
- ·Order history in Dashboard → Orders
Subscription System (Stripe)
Stripe billing for Indie and Pro tiers. Webhook handles subscription events. Tiers can be overridden manually with a billing note.
- ·Checkout and customer portal via Stripe-hosted pages
- ·Webhook handles checkout.session.completed, subscription updated/deleted, payment failed
- ·Tier enforced live from DB — no stale JWT values
- ·Manual tier overrides available
Email Infrastructure (Resend)
Transactional email via Resend. Silent no-op when RESEND_API_KEY is absent — safe in CI. Wired for password reset, beta approval, manuscript revision notifications, and deadline warnings.
- ·Forgot password: short-expiry single-use reset token
- ·Beta approval email with deadline date and direct read link
- ·Manuscript revised: all approved readers notified of new version
- ·Deadline warning email alongside in-app notification
Studio AI — User API Keys & Multi-Provider
Authors supply their own API key from Anthropic, OpenAI, or OpenRouter. Stream Facility does not provide AI access — authors pay their own provider directly. Keys are encrypted at rest and never returned to the client.
- ·Three providers: Anthropic, OpenAI, OpenRouter (200+ models via one key)
- ·Keys are encrypted at rest — only the model name and active provider are ever returned to the client; key values are never exposed
- ·Per-provider model selector with curated list (Sonnet, Opus, Haiku, GPT-4o, Gemini, Llama, Mistral…)
- ·Managed in Dashboard → Profile → AI Keys
- ·Chat and Write Assist automatically use whichever provider and model is active
Social — Friends & Favorites
Two-way friend connections with a request/accept/decline flow, and a favorites system for bookmarking books, series, and worlds. FriendButton and FavoriteButton are wired to public pages. Dashboard pages manage both.
- ·Symmetric friend request flow — send, accept, decline, cancel, or unfriend; either party can end the connection at any time
- ·FriendButton component: four states — Add Friend / Request Sent (cancel) / Accept+Decline / ✓ Friends (unfriend); wired to public author pages
- ·Any logged-in user can favorite any public world, book, or series; the heart button shows a live public count
- ·FavoriteButton: heart icon with count, unauthenticated users see sign-in link; wired to world, book, and series public pages
- ·Dashboard → Friends: pending received requests with Accept/Decline, friends list with Unfriend, sent requests with Cancel
- ·Dashboard → Favorites: items grouped by type (Books → Series → Worlds) with inline Remove
- ·Gift shop 'From your network' recipient tab remains deferred — infrastructure is ready
Reader Passage Highlights
Logged-in readers can highlight any passage while reading free books or beta manuscripts. Four colors, optional notes per highlight, highlights persist across sessions. Analytics stats bar and public sharing now built.
- ·Highlights are stored per-user and per-scene with optional notes; each highlight has a public/private toggle
- ·Text selection in ReadingTree triggers a floating HighlightToolbar; onMouseDown preventDefault preserves the browser selection while clicking toolbar buttons
- ·Four swatches: yellow #fde68a, green #a7f3d0, blue #bfdbfe, pink #fbcfe8 — click to save immediately; 'Note' mode adds textarea and re-picker before saving
- ·highlightSegments() injects <mark> elements into React-rendered TipTap prose without a DOM traversal — works on text nodes, handles multiple highlights per text node
- ·Dashboard → Highlights: analytics stats bar (total, books, most-marked, colors) computed client-side; Share/Public toggle per highlight via PATCH /api/highlights/[id]
- ·Public page at /highlights/[id] renders passage, note, book, scene, sharer — no auth required; generateMetadata for SEO title
Beta Cancellation: Reader Email
When an author cancels a beta campaign, every approved reader now receives a personalised email alongside the existing in-app notification. The email is sent via the platform Resend key.
- ·Email template added to the platform notification system for campaign cancellations
- ·The campaign cancellation flow now notifies all affected readers by email before expiring their access
- ·In-app notification still fires as before; email adds a second channel
Codex: Aliases, Map Tab & Entity Pages on Additional Entity Types
Factions, Creatures, Items, and Organizations now match the depth of Characters and Locations: reveal-gated name aliases, a Map tab for pinning entities on the world map, and an Entity Pages tab for period snapshots.
- ·AliasInput with revealedIn/revealChapter gating added to Factions, Creatures, Items, and Organizations
- ·MapTab added to all four entity types alongside existing Characters, Locations, and Regions
- ·EntityPagesTab added to Factions, Creatures, Items, Organizations, and Locations
- ·TimelineTab added to Organizations (was the only entity type missing it)
- ·API PUT handlers for all four entity types updated with aliases validation block
In-App Notification System
A complete in-app notification system: bell dropdown in the nav (with count badge and 5-item preview) and a full notifications page. Six event types trigger notifications — follows, friend requests, friend accepts, beta comments, beta replies, and beta applications.
- ·NotificationBell in InsideNav: fetches announcements + 5 most recent notifications on mount; red dot when unread; dropdown closes and marks read on link click
- ·TYPE_ICON map: ◎ beta_comment/reply, ◇ beta_application, ✦ friend_request/accepted, ↑ follow, ♦ gift_received, ✓ purchase_confirmed, ★ gift_redeemed
- ·follow notification fires on follow, resolves profile link from slug + isAuthor
- ·friend_request notification fires on POST /api/friends; friend_accepted fires on accept in PUT /api/friends/[id]
- ·beta_reply fan-out: author reply to a scene triggers insertMany to all readers who commented on that scene
- ·/dashboard/notifications page: full list, auto mark-all-read after 800ms, relative timestamps, unread badge
- ·Notifications card added to Dashboard home Social section (now 4-column grid)
- ·Daily digest cron already integrated — digestSentAt prevents double-sending
Studio: Scene-Level Date Anchoring & Structured WorldDates
In-World Date Anchoring extended to scene level. Acts, chapters, and scenes now store full structured WorldDate objects (not just label strings), enabling sort-key-based range queries for Write Assist and entity-level chat.
- ·◷ icon on scene rows in Plan view opens inline date panel — same era selector and begins/ends fields as act/chapter rows
- ·story_scenes gains eraId, eraName, inWorldBeginsDate/Label, inWorldEndsDate/Label
- ·story_acts and story_chapters gain inWorldBeginsDate/inWorldEndsDate WorldDate objects alongside existing label strings
- ·PATCH handlers compute inWorldBeginsSortKey / inWorldEndsSortKey via worldDateToSortKey() and store them for range queries
- ·resolveEntityState() utility merges the active entity page snapshot onto the base entity at a given world-date position — shared between Write Assist and Studio Chat
Studio Write Assist: World State Integration
Write Assist now delivers era-accurate codex context to the AI. Entity descriptions in the prompt reflect the entity's state at the scene's exact position in the world timeline — resolved via the active entity_pages snapshot, not the base profile.
- ·The AI generation route determines the scene's position in the world timeline from its anchored era and in-world date
- ·Each entity reference in context is resolved to its state at that exact world-date position
- ·The state resolver finds the active entity page snapshot covering that period and merges it onto the base entity profile
- ·If a character is a 'village guard' in Year 847 and a 'hero' in Year 850, the AI receives the Year 847 description when the scene is anchored there
Entity-Level AI Chat — World, Book, Series
World, book, and series detail pages now have a Chat tab for AI conversations anchored to that entity's full context — codex, draft structure, or series scope.
- ·Shared EntityChatView component with ContextGroup, ContextRef, MessageBubble types
- ·World chat: pulls from any of the 21 codex entity types, protected by an allowlist that prevents access to non-codex data
- ·Book chat: scoped to the book's linked world and draft structure
- ·Series chat: scoped to the series and its associated books
- ·All three routes use the author's configured AI provider via getAiConfig() and createChatStream() — same path as Studio Chat
- ·Chat history persisted per author per entity
Write Assist — Alias Reveal Gating
The AI generate route now filters aliases from entity context based on the author's current chapter position, preventing spoiler aliases from leaking into the prompt before they're narratively revealed.
- ·Entity context builder accepts the current book and chapter position, filtering aliases that haven't been narratively revealed yet
- ·Chapter position and draft book reference are resolved automatically and passed to the context builder
- ·Aliases without revealedIn, or gated to a different book, are always shown
Shop — Gift From Your Network
Gift certificate purchases can now target a friend directly from a 'From your network' tab in the recipient selector, without having to search by name or email.
- ·Two-tab switcher: Search (by name/email) and From your network
- ·Friends list loaded lazily from GET /api/friends on first tab switch
- ·Accepted friends appear as a scrollable list; clicking sets recipient and returns to the selected state
Story Pages — Global Browse
A new /dashboard/pages page lets authors see and filter all entity_pages records across their worlds in a single table.
- ·Server component with URL-driven type and world filters (chip tabs, no client-side state)
- ·Batch entity name lookup per collection; world name join
- ·Table columns: Entity, Type, World, Era, Date Range — responsive hidden columns
Entity Personas — Factions, Items, Locations
Factions and items can now have full persona aliases — separate codex entries representing cover identities, true names, or hidden forms — using the same reveal-gating model as character personas.
- ·New authenticated API endpoints for entity personas supporting factions, items, and locations
- ·Persona documents stored in the same collection with isPersona: true and parentId
- ·Factions and items list pages exclude personas via isPersona: { $ne: true } query filter
- ·Personas section on faction and item detail pages: inline creation form with name, role, description, reveal book
- ·Characters list page now shows a Personas subsection for the global character personas browse
This log tracks platform features, not individual content. For questions or feedback, get in touch.