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
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:
@@ -4,7 +4,6 @@ from datetime import date, datetime, timezone
|
||||
from sqlalchemy import or_, select
|
||||
|
||||
from scribe.models import async_session
|
||||
from scribe.models.event import Event
|
||||
from scribe.models.milestone import Milestone
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.note_draft import NoteDraft
|
||||
@@ -25,9 +24,10 @@ from scribe.models.user import User
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Backup format version. v3 (2026-06) added rulebooks/topics/rules + their
|
||||
# project subscription/suppression join tables, and events — all silently
|
||||
# dropped by v2. Bump when the serialized schema changes.
|
||||
BACKUP_VERSION = 3
|
||||
# project subscription/suppression join tables. v4 (2026-07) dropped events
|
||||
# when the calendar surface was retired — old v3 events are skipped on restore.
|
||||
# Bump when the serialized schema changes.
|
||||
BACKUP_VERSION = 4
|
||||
|
||||
# Tables intentionally NOT in the backup, surfaced in the payload so the gap is
|
||||
# explicit rather than silent. ACL (groups/shares) is a coherent follow-up;
|
||||
@@ -80,7 +80,6 @@ async def export_full_backup() -> dict:
|
||||
rulebooks = (await session.execute(select(Rulebook))).scalars().all()
|
||||
topics = (await session.execute(select(RulebookTopic))).scalars().all()
|
||||
rules = (await session.execute(select(Rule))).scalars().all()
|
||||
events = (await session.execute(select(Event))).scalars().all()
|
||||
subscriptions = (await session.execute(
|
||||
select(project_rulebook_subscriptions)
|
||||
)).all()
|
||||
@@ -245,27 +244,6 @@ async def export_full_backup() -> dict:
|
||||
"rulebook_subscriptions": _subscription_rows(subscriptions),
|
||||
"rule_suppressions": _rule_suppression_rows(rule_suppressions),
|
||||
"topic_suppressions": _topic_suppression_rows(topic_suppressions),
|
||||
"events": [
|
||||
{
|
||||
"id": e.id,
|
||||
"user_id": e.user_id,
|
||||
"project_id": e.project_id,
|
||||
"uid": e.uid,
|
||||
"caldav_uid": e.caldav_uid,
|
||||
"title": e.title,
|
||||
"start_dt": e.start_dt.isoformat() if e.start_dt else None,
|
||||
"duration_minutes": e.duration_minutes,
|
||||
"all_day": e.all_day,
|
||||
"description": e.description,
|
||||
"location": e.location,
|
||||
"color": e.color,
|
||||
"recurrence": e.recurrence,
|
||||
"reminder_minutes": e.reminder_minutes,
|
||||
"created_at": e.created_at.isoformat(),
|
||||
"updated_at": e.updated_at.isoformat(),
|
||||
}
|
||||
for e in events
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -314,9 +292,6 @@ async def export_user_backup(user_id: int) -> dict:
|
||||
rules = (await session.execute(
|
||||
select(Rule).where(or_(*rule_filters))
|
||||
)).scalars().all() if rule_filters else []
|
||||
events = (await session.execute(
|
||||
select(Event).where(Event.user_id == user_id)
|
||||
)).scalars().all()
|
||||
if project_ids:
|
||||
subscriptions = (await session.execute(
|
||||
select(project_rulebook_subscriptions).where(
|
||||
@@ -480,27 +455,6 @@ async def export_user_backup(user_id: int) -> dict:
|
||||
"rulebook_subscriptions": _subscription_rows(subscriptions),
|
||||
"rule_suppressions": _rule_suppression_rows(rule_suppressions),
|
||||
"topic_suppressions": _topic_suppression_rows(topic_suppressions),
|
||||
"events": [
|
||||
{
|
||||
"id": e.id,
|
||||
"user_id": e.user_id,
|
||||
"project_id": e.project_id,
|
||||
"uid": e.uid,
|
||||
"caldav_uid": e.caldav_uid,
|
||||
"title": e.title,
|
||||
"start_dt": e.start_dt.isoformat() if e.start_dt else None,
|
||||
"duration_minutes": e.duration_minutes,
|
||||
"all_day": e.all_day,
|
||||
"description": e.description,
|
||||
"location": e.location,
|
||||
"color": e.color,
|
||||
"recurrence": e.recurrence,
|
||||
"reminder_minutes": e.reminder_minutes,
|
||||
"created_at": e.created_at.isoformat(),
|
||||
"updated_at": e.updated_at.isoformat(),
|
||||
}
|
||||
for e in events
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -591,17 +545,17 @@ async def _restore_v1(data: dict) -> dict:
|
||||
async def _restore_v2(data: dict) -> dict:
|
||||
"""Restore v2/v3 backup with full FK re-mapping.
|
||||
|
||||
Conversations + push subscriptions in pre-pivot backups are silently
|
||||
skipped — those subsystems were removed in the MCP-first pivot. v3-only
|
||||
sections (rulebooks/topics/rules/join-tables/events) are guarded by
|
||||
data.get so a v2 payload restores without them.
|
||||
Conversations, push subscriptions, and (as of v4) events in older backups
|
||||
are silently skipped — those subsystems were removed. v3+ sections
|
||||
(rulebooks/topics/rules/join-tables) are guarded by data.get so a v2
|
||||
payload restores without them.
|
||||
"""
|
||||
stats: dict[str, int] = {
|
||||
"users": 0, "projects": 0, "milestones": 0, "notes": 0,
|
||||
"task_logs": 0, "note_drafts": 0, "note_versions": 0,
|
||||
"settings": 0, "rulebooks": 0, "rulebook_topics": 0, "rules": 0,
|
||||
"rulebook_subscriptions": 0, "rule_suppressions": 0,
|
||||
"topic_suppressions": 0, "events": 0,
|
||||
"topic_suppressions": 0,
|
||||
}
|
||||
|
||||
async with async_session() as session:
|
||||
@@ -860,31 +814,6 @@ async def _restore_v2(data: dict) -> dict:
|
||||
))
|
||||
stats["topic_suppressions"] += 1
|
||||
|
||||
# 15. Events (v3)
|
||||
for e_data in data.get("events", []):
|
||||
mapped_uid = user_id_map.get(e_data.get("user_id", 0))
|
||||
if mapped_uid is None:
|
||||
continue
|
||||
ev = Event(
|
||||
user_id=mapped_uid,
|
||||
project_id=project_id_map.get(e_data["project_id"]) if e_data.get("project_id") else None,
|
||||
uid=e_data.get("uid", ""),
|
||||
caldav_uid=e_data.get("caldav_uid", ""),
|
||||
title=e_data.get("title", ""),
|
||||
start_dt=_dt(e_data.get("start_dt")),
|
||||
duration_minutes=e_data.get("duration_minutes"),
|
||||
all_day=e_data.get("all_day", False),
|
||||
description=e_data.get("description", ""),
|
||||
location=e_data.get("location", ""),
|
||||
color=e_data.get("color", ""),
|
||||
recurrence=e_data.get("recurrence"),
|
||||
reminder_minutes=e_data.get("reminder_minutes"),
|
||||
created_at=_dt(e_data.get("created_at")),
|
||||
updated_at=_dt(e_data.get("updated_at")),
|
||||
)
|
||||
session.add(ev)
|
||||
stats["events"] += 1
|
||||
|
||||
await session.commit()
|
||||
|
||||
logger.info("Restored v2/v3 backup: %s", stats)
|
||||
|
||||
Reference in New Issue
Block a user