refactor(scribe): retire calendar/events + person/place/list entities (backend)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 11s
CI & Build / integration (push) Successful in 30s
CI & Build / Python tests (push) Failing after 31s
CI & Build / Build & push image (push) Has been skipped

Narrow Scribe to a Claude-Code work system-of-record (milestone #194,
decision note #1759). Wholesale removal per rule #22 — backend + schema half.

Calendar/events + CalDAV: delete models/event, services/{events,caldav,
caldav_sync}, routes/events, mcp/tools/events; strip event branches from
backup (bump v3->v4), dashboard (upcoming_events), trash, recent, and the
mcp server read-only allowlist + instructions.

Typed entities (person/place/list): delete mcp/tools/entities; drop the
notes.metadata (entity_meta) column from model/service/routes and the
knowledge browse service. note_type STAYS — it also marks 'process' notes.

Scheduler: event_scheduler -> recurrence_scheduler, keeping only the
recurring-task spawn job (drops event reminders + CalDAV sync).

Schema: migration 0069 drops the events table + notes.metadata column +
orphan caldav settings rows (faithful downgrade recreates them).

KEEP: recurrence.py (task recurrence), notifications task reminders, graph
view, and every work surface. Frontend + plugin/docs true-up follow next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BPtbSzA4JLMAKgFZ8VTg7Q
This commit is contained in:
2026-07-19 13:29:14 -04:00
parent f6629d4bcf
commit b49efdcb11
33 changed files with 194 additions and 3453 deletions
@@ -0,0 +1,82 @@
"""drop events table + notes.metadata column (retire calendar + entity surfaces)
Revision ID: 0069
Revises: 0068
Create Date: 2026-07-19
The personal-assistant surfaces (calendar/events + CalDAV, and the typed
person/place/list entities that stored structured fields in notes.metadata)
were removed when Scribe narrowed to a Claude-Code work system-of-record.
This migration drops their storage:
- the `events` table (all calendar/CalDAV data)
- the `notes.metadata` (entity_meta) JSONB column — it only ever held
person/place/list structured fields. The `note_type` column STAYS: it
also distinguishes 'process' notes.
- orphan CalDAV settings rows (nothing reads them after the removal)
Downgrade recreates the table + column structure at its pre-removal shape.
The dropped data itself is not recoverable.
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import JSONB
revision = "0069"
down_revision = "0068"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Entity metadata column (person/place/list structured fields). The
# note_type column is intentionally kept — it also marks 'process' notes.
op.drop_column("notes", "metadata")
# Calendar / CalDAV storage. Dropping the table drops its indexes + the
# duration CHECK constraint with it.
op.drop_table("events")
# Orphan CalDAV integration settings — no code reads them post-removal.
op.execute("DELETE FROM settings WHERE key LIKE 'caldav%'")
def downgrade() -> None:
# Recreate the events table at its pre-removal schema (empty — the data is
# gone). Mirrors the model as of 0037 (reminders) + 0043 (duration_minutes)
# + 0057 (soft-delete columns/index).
op.create_table(
"events",
sa.Column("id", sa.Integer(), primary_key=True),
sa.Column("user_id", sa.Integer(),
sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
sa.Column("project_id", sa.Integer(),
sa.ForeignKey("projects.id", ondelete="SET NULL"), nullable=True),
sa.Column("uid", sa.Text(), nullable=False),
sa.Column("title", sa.Text(), nullable=False, server_default=""),
sa.Column("start_dt", sa.DateTime(timezone=True), nullable=False),
sa.Column("duration_minutes", sa.Integer(), nullable=True),
sa.Column("all_day", sa.Boolean(), nullable=False, server_default=sa.false()),
sa.Column("description", sa.Text(), nullable=False, server_default=""),
sa.Column("location", sa.Text(), nullable=False, server_default=""),
sa.Column("caldav_uid", sa.Text(), nullable=False, server_default=""),
sa.Column("color", sa.Text(), nullable=False, server_default=""),
sa.Column("recurrence", sa.Text(), nullable=True),
sa.Column("reminder_minutes", sa.Integer(), nullable=True),
sa.Column("reminder_sent_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True),
nullable=False, server_default=sa.func.now()),
sa.Column("updated_at", sa.DateTime(timezone=True),
nullable=False, server_default=sa.func.now()),
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("deleted_batch_id", sa.Text(), nullable=True),
sa.CheckConstraint(
"duration_minutes IS NULL OR duration_minutes >= 0",
name="events_duration_minutes_non_negative",
),
)
op.create_index("ix_events_deleted_at", "events", ["deleted_at"])
# Re-add the entity metadata column.
op.add_column("notes", sa.Column("metadata", JSONB(), nullable=True))