Merge Workstream C: unlocks + inventory items + sigils + essence

This commit is contained in:
Indiana
2026-07-24 09:41:55 +00:00
13 changed files with 1039 additions and 3 deletions

View File

@@ -3,6 +3,9 @@ from app.models.contact_session import ContactSession
from app.models.entity import Entity
from app.models.entity_sighting import EntitySighting
from app.models.event import Event
from app.models.inventory_item import InventoryItem
from app.models.sigil import Sigil
from app.models.unlock import UnlockRecord
from app.models.user import User
from app.models.waitlist_entry import WaitlistEntry
@@ -13,5 +16,8 @@ __all__ = [
"Entity",
"EntitySighting",
"Event",
"InventoryItem",
"Sigil",
"UnlockRecord",
"WaitlistEntry",
]

View File

@@ -0,0 +1,24 @@
import uuid
from datetime import datetime, timezone
from sqlalchemy import DateTime, ForeignKey, String
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column
from app.db import Base
class InventoryItem(Base):
"""An item a seeker has been dropped (see `app.inventory.roll_item_drop`)
— a Reliquary trinket, not a purchased unlock."""
__tablename__ = "inventory_items"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"), index=True)
item_type: Mapped[str] = mapped_column(String(32), index=True)
item_key: Mapped[str] = mapped_column(String(64))
payload: Mapped[dict] = mapped_column(JSONB, default=dict)
obtained_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

View File

@@ -0,0 +1,25 @@
import uuid
from datetime import datetime, timezone
from sqlalchemy import DateTime, ForeignKey, String
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column
from app.db import Base
class Sigil(Base):
"""A seeker-designed sigil (Workstream F's constrained geometric
builder). `design` is placeholder-shaped `{"points": [[x,y],...],
"rune": str}` pending the frontend sigil-designer workstream landing —
see `app.inventory.validate_sigil_design`."""
__tablename__ = "sigils"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"), index=True)
name: Mapped[str] = mapped_column(String(64))
design: Mapped[dict] = mapped_column(JSONB, default=dict)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

View File

@@ -0,0 +1,21 @@
import uuid
from datetime import datetime, timezone
from sqlalchemy import DateTime, ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column
from app.db import Base
class UnlockRecord(Base):
"""A permanent unlock a seeker has purchased with essence (e.g. the
listening tool). One row per (user, unlock_key)."""
__tablename__ = "unlocks"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id"), index=True)
unlock_key: Mapped[str] = mapped_column(String(64), index=True)
unlocked_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

View File

@@ -1,7 +1,7 @@
import uuid
from datetime import datetime, timezone
from sqlalchemy import DateTime, String
from sqlalchemy import DateTime, Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from app.db import Base
@@ -14,6 +14,13 @@ class User(Base):
username: Mapped[str] = mapped_column(String(32), unique=True, index=True)
password_hash: Mapped[str] = mapped_column(String(255))
email: Mapped[str | None] = mapped_column(String(255), nullable=True)
# NOTE: owned by Workstream B in the character-depth-ghost-log spec
# (docs/superpowers/specs/2026-07-23-character-depth-ghost-log-design.md).
# Added here so Workstream C (unlocks/items/sigils/drops) can build and
# test against it in isolation; the merge controller reconciles this
# against Workstream B's own edit to this file (which will also add
# `favor: float` per the spec's Contract section).
essence: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)