From 70ab3f38c602a3fcae169a199ebdfff898f92012 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 3 Jun 2026 16:16:44 -0400 Subject: [PATCH] chore: remove pre-pivot dead code + finish Scribe rebrand (#599 t1-3) - Header wordmark Fabled -> Scribe; fable:calendar-changed event -> scribe:calendar-changed; SettingsView CSS comment. - Drop dead Project.auto_summary + summary_updated_at columns (migration 0063) -- the Ollama-era summarizer is gone; model + 2 frontend types + projects test updated. - Remove pivot vestiges: diagnostics _curator_busy()/curator_busy heartbeat field, tz BRIEFING_DAY_START_HOUR/user_briefing_date dead aliases, the ignored 'model' param on get_embedding (+ its test). ruff src/ clean; CI is the gate. Part of scribe plan #599. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../0063_drop_project_auto_summary.py | 34 +++++++++++++++++++ frontend/src/components/AppHeader.vue | 2 +- frontend/src/views/CalendarView.vue | 4 +-- frontend/src/views/ProjectListView.vue | 1 - frontend/src/views/ProjectView.vue | 1 - frontend/src/views/SettingsView.vue | 2 +- src/scribe/models/project.py | 8 +---- src/scribe/services/diagnostics.py | 19 ++++------- src/scribe/services/embeddings.py | 5 +-- src/scribe/services/tz.py | 13 ++----- tests/test_embeddings.py | 16 --------- tests/test_mcp_tool_projects.py | 2 +- 12 files changed, 50 insertions(+), 57 deletions(-) create mode 100644 alembic/versions/0063_drop_project_auto_summary.py diff --git a/alembic/versions/0063_drop_project_auto_summary.py b/alembic/versions/0063_drop_project_auto_summary.py new file mode 100644 index 0000000..527bef9 --- /dev/null +++ b/alembic/versions/0063_drop_project_auto_summary.py @@ -0,0 +1,34 @@ +"""drop dead Project.auto_summary columns + +Revision ID: 0063 +Revises: 0062 +Create Date: 2026-06-03 + +auto_summary + summary_updated_at were written by generate_project_summary +(Ollama), removed in the MCP pivot. Nothing has populated them since; the +field was stale-or-NULL. Drop the columns. +""" +from alembic import op +import sqlalchemy as sa + + +revision = "0063" +down_revision = "0062" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.drop_column("projects", "auto_summary") + op.drop_column("projects", "summary_updated_at") + + +def downgrade() -> None: + op.add_column( + "projects", + sa.Column("summary_updated_at", sa.DateTime(timezone=True), nullable=True), + ) + op.add_column( + "projects", + sa.Column("auto_summary", sa.Text(), nullable=True), + ) diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue index 34c4ff1..a61689e 100644 --- a/frontend/src/components/AppHeader.vue +++ b/frontend/src/components/AppHeader.vue @@ -39,7 +39,7 @@ router.afterEach(() => { - Fabled + Scribe diff --git a/frontend/src/views/CalendarView.vue b/frontend/src/views/CalendarView.vue index 6f40a40..54718f3 100644 --- a/frontend/src/views/CalendarView.vue +++ b/frontend/src/views/CalendarView.vue @@ -168,11 +168,11 @@ function onCalendarChanged() { onMounted(() => { document.addEventListener("mousedown", onDocClick); - document.addEventListener("fable:calendar-changed", onCalendarChanged); + document.addEventListener("scribe:calendar-changed", onCalendarChanged); }); onUnmounted(() => { document.removeEventListener("mousedown", onDocClick); - document.removeEventListener("fable:calendar-changed", onCalendarChanged); + document.removeEventListener("scribe:calendar-changed", onCalendarChanged); }); // ── Calendar callbacks ───────────────────────────────────────────────────── diff --git a/frontend/src/views/ProjectListView.vue b/frontend/src/views/ProjectListView.vue index 05448d6..4c38dfe 100644 --- a/frontend/src/views/ProjectListView.vue +++ b/frontend/src/views/ProjectListView.vue @@ -22,7 +22,6 @@ interface Project { goal: string | null; status: "active" | "paused" | "completed" | "archived"; color: string | null; - auto_summary: string | null; permission?: string; is_shared?: boolean; created_at: string; diff --git a/frontend/src/views/ProjectView.vue b/frontend/src/views/ProjectView.vue index 4690f9a..cf9e182 100644 --- a/frontend/src/views/ProjectView.vue +++ b/frontend/src/views/ProjectView.vue @@ -37,7 +37,6 @@ interface Project { goal: string | null; status: "active" | "paused" | "completed" | "archived"; color: string | null; - auto_summary: string | null; permission?: string; created_at: string; updated_at: string; diff --git a/frontend/src/views/SettingsView.vue b/frontend/src/views/SettingsView.vue index 25c2c19..3d5e762 100644 --- a/frontend/src/views/SettingsView.vue +++ b/frontend/src/views/SettingsView.vue @@ -3076,7 +3076,7 @@ function formatUserDate(iso: string): string { .settings-empty { opacity: 0.5; margin-top: 1rem; } .settings-description { opacity: 0.7; margin-bottom: 1rem; line-height: 1.5; } -/* Fable MCP section */ +/* Scribe MCP section */ .mcp-status { opacity: 0.6; font-size: 0.9rem; } .mcp-unavailable p { opacity: 0.7; } .mcp-available { display: flex; flex-direction: column; gap: 1.25rem; } diff --git a/src/scribe/models/project.py b/src/scribe/models/project.py index 5d894c0..8ccacc5 100644 --- a/src/scribe/models/project.py +++ b/src/scribe/models/project.py @@ -1,6 +1,5 @@ import enum -from datetime import datetime -from sqlalchemy import DateTime, ForeignKey, Integer, Text +from sqlalchemy import ForeignKey, Integer, Text from sqlalchemy.orm import Mapped, mapped_column from scribe.models import Base from scribe.models.base import TimestampMixin, SoftDeleteMixin @@ -22,10 +21,6 @@ class Project(Base, TimestampMixin, SoftDeleteMixin): goal: Mapped[str] = mapped_column(Text, default="") status: Mapped[str] = mapped_column(Text, default="active") color: Mapped[str | None] = mapped_column(Text, nullable=True) # hex color - auto_summary: Mapped[str | None] = mapped_column(Text, nullable=True, default=None) - summary_updated_at: Mapped[datetime | None] = mapped_column( - DateTime(timezone=True), nullable=True, default=None - ) def to_dict(self) -> dict: return { @@ -36,7 +31,6 @@ class Project(Base, TimestampMixin, SoftDeleteMixin): "goal": self.goal, "status": self.status, "color": self.color, - "auto_summary": self.auto_summary, "created_at": self.created_at.isoformat(), "updated_at": self.updated_at.isoformat(), } diff --git a/src/scribe/services/diagnostics.py b/src/scribe/services/diagnostics.py index e355b86..28ca44f 100644 --- a/src/scribe/services/diagnostics.py +++ b/src/scribe/services/diagnostics.py @@ -5,11 +5,10 @@ clear cause in the logs. This module adds three things designed to make the crash class identifiable from logs alone: 1. **Heartbeat** — once per minute, log a snapshot of process resources - (RSS memory, asyncio task count, DB pool checked-in/out, curator - busy state). A sudden silence in heartbeats lets you bound the - crash time to within a minute, and the last snapshot before silence - usually rules in or out memory growth / pool exhaustion / hung - curator pass. + (RSS memory, asyncio task count, DB pool checked-in/out). A sudden + silence in heartbeats lets you bound the crash time to within a + minute, and the last snapshot before silence usually rules in or out + memory growth / pool exhaustion. 2. **Signal handler** — catches SIGTERM and SIGINT, logs them with the sender's intent ("docker stop", "swarm restart", "manual ctrl-C") @@ -108,11 +107,6 @@ def _asyncio_task_count() -> int | None: return None -def _curator_busy() -> bool | None: - # Curator was removed in Phase 8 of the MCP-first pivot. Always False. - return False - - def _uptime_secs() -> float | None: if _started_at is None: return None @@ -134,7 +128,6 @@ def _snapshot_dict(reason: str = "heartbeat") -> dict[str, Any]: "rss_mb": _process_rss_mb(), "asyncio_tasks": _asyncio_task_count(), "db_pool": _db_pool_stats(), - "curator_busy": _curator_busy(), "pid": os.getpid(), } @@ -284,9 +277,9 @@ async def _heartbeat_loop() -> None: _persistent_log( logging.INFO, "diag heartbeat: uptime=%ss rss=%sMB asyncio_tasks=%s " - "db_pool=%s curator_busy=%s", + "db_pool=%s", snap["uptime_secs"], snap["rss_mb"], snap["asyncio_tasks"], - snap["db_pool"], snap["curator_busy"], + snap["db_pool"], ) _write_state_atomic(_CURRENT_STATE_PATH, snap) except Exception: diff --git a/src/scribe/services/embeddings.py b/src/scribe/services/embeddings.py index be130d4..e9eaf2e 100644 --- a/src/scribe/services/embeddings.py +++ b/src/scribe/services/embeddings.py @@ -53,12 +53,9 @@ async def _get_model(): return _model -async def get_embedding(text: str, model: str | None = None) -> list[float]: +async def get_embedding(text: str) -> list[float]: """Get an embedding vector for the given text. - The ``model`` parameter is preserved for backward compatibility with the - Ollama era but is now ignored — fastembed uses a single fixed model. - Raises if the fastembed model fails to load. Callers should catch and degrade to keyword search. """ diff --git a/src/scribe/services/tz.py b/src/scribe/services/tz.py index e6f50c8..4c148cd 100644 --- a/src/scribe/services/tz.py +++ b/src/scribe/services/tz.py @@ -13,14 +13,11 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError from scribe.services.settings import get_setting -# Day-rollover boundary for journal/day-anchored views. The "day" flips at +# Day-rollover boundary for day-anchored "today" logic. The "day" flips at # this local hour (not midnight) so the 00:00–04:00 local window still # shows yesterday's content until the user "starts" the new day. DAY_ROLLOVER_HOUR = 4 -# Backwards-compat alias for code paths still referencing the old name. -BRIEFING_DAY_START_HOUR = DAY_ROLLOVER_HOUR - async def get_user_tz(user_id: int) -> ZoneInfo: """Return the user's IANA ``ZoneInfo``, falling back to UTC.""" @@ -41,12 +38,8 @@ async def user_day_date(user_id: int) -> date: """Return the current "day" in the user's local timezone. The day flips at ``DAY_ROLLOVER_HOUR`` (4am local) rather than midnight, - so the 00:00–04:00 local window still returns *yesterday* — the user's - journal stays anchored to yesterday until they cross the rollover. + so the 00:00–04:00 local window still returns *yesterday* — a day-anchored + view stays on yesterday until the user crosses the rollover. """ tz = await get_user_tz(user_id) return (datetime.now(tz) - timedelta(hours=DAY_ROLLOVER_HOUR)).date() - - -# Backwards-compat alias used by briefing-only modules being torn down in Stage B. -user_briefing_date = user_day_date diff --git a/tests/test_embeddings.py b/tests/test_embeddings.py index e887170..ffaf4b1 100644 --- a/tests/test_embeddings.py +++ b/tests/test_embeddings.py @@ -62,22 +62,6 @@ async def test_get_embedding_returns_list_of_floats(): fake_embedder.embed.assert_called_once_with(["hello world"]) -@pytest.mark.asyncio -async def test_get_embedding_ignores_legacy_model_param(): - """The `model` kwarg is preserved for backward-compat but should not affect - the fastembed call.""" - fake_vec = MagicMock() - fake_vec.tolist.return_value = [0.0] - fake_embedder = MagicMock() - fake_embedder.embed = MagicMock(return_value=iter([fake_vec])) - with patch( - "scribe.services.embeddings._get_model", - AsyncMock(return_value=fake_embedder), - ): - out = await get_embedding("x", model="ignored-model-name") - assert out == [0.0] - - @pytest.mark.asyncio async def test_get_embedding_propagates_model_load_failures(): """If fastembed can't initialize, the error propagates — callers catch diff --git a/tests/test_mcp_tool_projects.py b/tests/test_mcp_tool_projects.py index 9ef87b9..5328f74 100644 --- a/tests/test_mcp_tool_projects.py +++ b/tests/test_mcp_tool_projects.py @@ -20,7 +20,7 @@ def _bind_user(): def _fake_project(**overrides) -> MagicMock: p = MagicMock() base = {"id": 1, "title": "P", "description": "", "goal": "", - "status": "active", "color": None, "auto_summary": None} + "status": "active", "color": None} base.update(overrides) p.to_dict.return_value = base return p