Skip to content

ship a Reality universe and give the universe record a factual flag #7616

Description

@atomantic

Problem / Goal

There is no factual universe in PortOS, and none ships:

  • The universe record (server/services/universeBuilder/sanitize.js:683-790) has no factual/fiction axis. Canon trunks are characters/places/objects; category kinds are the same three plus other.
  • createUniverse mints randomUUID() and accepts no caller id (server/services/universeBuilder/crud.js:244-247).
  • data.reference/ contains no universes/ directory; bootstrapServices reads data.reference only for providers.json (server/services/bootstrap.js:264). Universes are PG rows (server/lib/db/schema/universes.js:19), so setup-data.js is not the seeding mechanism — a migration is.
  • Repo-wide there is no factual / non-fiction / isFiction concept on a scrap, an ingredient, or a universe.

Goal: factual/autobiographical capture has a home from first boot, and the universe carries the flag that tells the extractor (landed in #7609) how to read the text.

Approach

  1. Add factual: boolean to the universe record in sanitizeTemplate (server/services/universeBuilder/sanitize.js:683-790), persisted only when true so every existing record keeps its on-disk shape and wire checksum.
  2. Bump universes 11 → 12 in server/lib/schemaVersions.js:82, with the rationale inline: a ≤v11 peer strips factual, then LWW's the stripped record back and silently reclassifies the user's Reality universe as fiction. Update the three assertions in server/lib/schemaVersions.test.js (:27, :64, :71).
  3. Seed migration scripts/migrations/NNN-reality-universe-seed.js: a universe with the deterministic id universe-reality (matches UNIVERSE_ID_RE = /^[A-Za-z0-9-]{8,80}$/, sanitize.js:51), name: 'Reality', factual: true, ephemeral: false, and empty logline / premise / styleNotes. Shipping invented copy for the user's own life is wrong, and an LLM call at migration time is barred by the AI Provider Usage Policy in AGENTS.md.
    • Deterministic id so a user's federated machines converge on ONE Reality by LWW instead of accumulating one per install.
    • Gate on absence INCLUDING tombstones. insertUniverseWithId already exists (server/services/universeBuilder/crud.js:319) and takes a caller-supplied id — but it deliberately overwrites a tombstone (wasResurrection at :326-334), which is right for share-bucket re-import and wrong here. So the migration reads await store().loadOne('universe-reality') first and no-ops when any row exists, deleted true or false, only calling insertUniverseWithId when there is none. A user who deleted Reality must not have it resurrected on the next upgrade. Do not add a resurrect: false option — the check belongs in the migration, not in a shared importer path.
  4. Surface factual in the Universe Builder settings panel as a "This is a real-world universe" toggle, and badge it in the universe list.

Acceptance criteria

  • A fresh install ends up with exactly one live universe named Reality, id universe-reality, factual: true. Re-running the migration is a no-op.
  • An install where the user soft-deleted Reality does not get it back — covered by a migration test with a tombstoned row present.
  • PORTOS_SCHEMA_VERSIONS.universes is 12; server/lib/schemaVersions.test.js and server/lib/db.ddlParity.test.js pass, and the existing version-gate coverage guards still hold.
  • factual is absent from the persisted record when false, so an existing universe's wire checksum is unchanged — a test pins this.
  • The Universe Builder toggle round-trips and the list badge renders; follow client/src/AGENTS.md.
  • docs/STORAGE.md and the catalog docs describe the shipped Reality universe.

Notes

This is independently shippable, but it is what makes 7615's Reality default mean anything — land them close together.

Also fold in here

Phase 1 (#7609, PR #7614) marks factual rows with factual / real-person
tags. normalizeTags treats those like any user tag — canonicalized by key,
user-editable, user-deletable, federated with the row — so today the only
machine-readable marker that a catalog row describes a real person is a string
the user can rename away. Once this phase lands a real factual field, demote
those two tags to a display affordance derived from it rather than the source of
truth. Do NOT write a migration that strips existing ones: they are
indistinguishable from tags the user typed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:createUniverse/catalog/Create-suiteeffort:mediumDispatch effort: mediumenhancementNew feature or requestin-progressClaimed and being workedmodel:heavyDispatch capability: heavyplanTracked by /do:replanplanner:opus-5Plan authored by the opus-5 model

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions