feat([issue-4188]): persist a universe's linked mood board on the record - #4192
Merged
Conversation
Phase 1 of #4188 (mood boards as a universe style tool). Adds an optional moodBoardId to the universe record — sanitized, PATCHable, cleared by ''/null, persisted only when set — and bumps the universes sync gate to v9 so an older peer can't strip-then-LWW the link away (the pointer rides the wire since mood boards federate). The Universe Bible tab's reference strip becomes a controlled, persisted picker (with one-click create-and-link named after the universe) instead of a per-browser localStorage pick; Pipeline keeps the localStorage mode.
…s and expand auto-saves Two P1s from the codex pass: (1) a pre-v9 peer's edit passes the version gate (only AHEAD rejects) and its moodBoardId-unaware sanitized record would LWW-strip the local link — mergeUniversesFromSync now disambiguates omitted-vs-cleared by the sender's universes schema version, threaded from both the snapshot and per-record push receive paths; (2) the expand/refine auto-save payloads omitted moodBoardId, so Generate From Idea on a new universe created it unlinked and dropped the pick on rehydration.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 1 of #4188 (mood boards as a universe style-building tool): the universe record gains a persisted
moodBoardIdlink, replacing the localStorage-only mood-board reference in the Universe Builder.sanitizeTemplateaccepts an optionalmoodBoardIdpointer (mb-<uuid>, capped atMOOD_BOARD_ID_MAX), persisted only when set so existing records keep their on-disk shape. Accepted on create and PATCH (PATCHABLE_SCALARS); key-present with''/nullclears, key-absent preserves.universesschema version bumped 8 → 9. The pointer deliberately rides the wire (mood boards federate, so the id resolves on peers), and per the Structured character relationships + opposing-force tagging #1287/Object↔character emotional attachment / significance model #1288 precedent an additive wire-riding field must be version-gated or an older peer re-sanitizing the record would strip-then-LWW the link away. (The issue body guessed "no bump needed" — the codebase precedent says otherwise, so this deviates from the issue text there.) Hardcoded version fixtures in the sync/manifest tests updated. The field is hashed for conflict detection but not restorable — same treatment asmediaCollection.universeId(structural link).MoodBoardReferenceStripgains a controlled mode (value/onChange): selection is owned by the caller, never falls back to the first board (a deleted board shows as unset rather than silently drifting), and offers one-click create-and-link named after the universe (newBoardName). The Universe Bible tab drives it from the draft (draft.moodBoardId), persisted through the normal dirty-gated Save.Pipeline.jsxkeeps the uncontrolled localStorage mode unchanged.A codex review pass surfaced two P1s, fixed in the second commit:
moodBoardId-unaware sanitized record would erase the local link.mergeUniversesFromSyncnow disambiguates omitted-vs-cleared by the sender'suniversesschema version (senderSchemaVersions, threaded from both the snapshot path indataSync.applyRemoteand the per-record push path inpeerSyncReceive): a pre-v9/no-meta sender's omission preserves the local link; a v9-aware sender's omission is an explicit clear.useUniverseExpand's create/update payloads omittedmoodBoardId, so "Generate From Idea" on a new universe created it unlinked and rehydration silently dropped the pick.Remaining phases of #4188 (gallery/upload pickers + video items, per-item prompt-from-media analysis, board→style synthesis) are not in this PR — see the reconciliation comment on the issue.
Refs #4188
Test plan
server/services/universeBuilder.test.js— newmoodBoardIdsuite: persists through create, absent when never set, PATCH set/preserve-on-absent/clear-on-''-and-null, non-string sanitized to absent on read; merge tests for behind-peer preserve, v9 explicit clear, and v9 apply-over-local.server/services/dataSync.pipelineUniverse.test.js— route-through test: a v8 sender's snapshot edit cannot stripmoodBoardId, a v9 sender's omission clears it.peerSync.test.jscall-args assertions extended with the threadedsenderSchemaVersions.server/lib/schemaVersions.test.js+ sync fixtures (dataSync.pipelineUniverse,peerSync,integration) updated for v9; ahead/behind gating asserted at v10-vs-v9.client/src/components/moodBoard/MoodBoardReferenceStrip.test.jsx— new: uncontrolled fallback + localStorage persistence unchanged; controlled mode no-fallback, onChange routing, no localStorage writes, deleted-board-shows-unset, create-and-link (with and without existing boards).biome lintclean.