From eed4551497e8b59078b058df6fa8e515d4a20228 Mon Sep 17 00:00:00 2001 From: Renn F Date: Tue, 12 May 2026 04:03:21 +0200 Subject: [PATCH] feat(alembic): B2 drop unused pm_approvals Task column Smoke run analysis initially flagged three Task fields as unused (pm_approvals, quick_context, proactive_context). A follow-up audit found quick_context (stores original_developer marker + doc notes + PR creator + escalation notes) and proactive_context (RAG injection) are actively used. Only pm_approvals is truly orphaned. Migration 014 drops pm_approvals; downgrade() recreates it if ever needed. The two false-positive fields stay untouched. Spec ref: docs/superpowers/specs/2026-05-12-post-smoke-3-fixes-design.md section B2 (re-scoped 2026-05-12). --- alembic/versions/014_drop_pm_approvals.py | 34 +++++++++++++++ roboco/api/schemas/tasks.py | 4 -- roboco/db/tables.py | 3 -- roboco/models/task.py | 7 ---- tests/conftest.py | 1 - .../test_migration_014_drop_pm_approvals.py | 41 +++++++++++++++++++ tests/unit/api/test_schemas_tasks.py | 1 - tests/unit/services/test_audit_agent_id.py | 1 - 8 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 alembic/versions/014_drop_pm_approvals.py create mode 100644 tests/integration/test_migration_014_drop_pm_approvals.py diff --git a/alembic/versions/014_drop_pm_approvals.py b/alembic/versions/014_drop_pm_approvals.py new file mode 100644 index 00000000..cfd9f19c --- /dev/null +++ b/alembic/versions/014_drop_pm_approvals.py @@ -0,0 +1,34 @@ +"""Drop unused Task column pm_approvals. + +Smoke run analysis (2026-05-12) initially flagged three Task fields +as unused: pm_approvals, quick_context, proactive_context. A follow-up +audit found quick_context and proactive_context are actively written +(original_developer marker, doc_notes, PR creator tags; RAG context +injection respectively). Only pm_approvals is truly orphaned — zero +writers, only model + schema declarations as readers. + +Revision ID: 014_drop_pm_approvals +Revises: 013_drop_role_enum +Create Date: 2026-05-12 +""" + +from __future__ import annotations + +import sqlalchemy as sa +from alembic import op + +revision = "014_drop_pm_approvals" +down_revision = "013_drop_role_enum" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.drop_column("tasks", "pm_approvals") + + +def downgrade() -> None: + op.add_column( + "tasks", + sa.Column("pm_approvals", sa.JSON, nullable=False, server_default="{}"), + ) diff --git a/roboco/api/schemas/tasks.py b/roboco/api/schemas/tasks.py index b7a3039a..fac702b9 100644 --- a/roboco/api/schemas/tasks.py +++ b/roboco/api/schemas/tasks.py @@ -269,9 +269,6 @@ class TaskResponse(BaseModel): docs_complete: bool = False # Documenter has finished pr_created: bool = False # Developer has created PR - # PM Approval Tracking (for AWAITING_PM_REVIEW phase) - pm_approvals: dict[str, bool] = {} # e.g. {'main_pm': True, 'cell_pm': True} - # Ownership team: Team created_by: UUID @@ -622,7 +619,6 @@ def task_to_response(task: "TaskTable") -> TaskResponse: ), docs_complete=task.docs_complete, pr_created=task.pr_created, - pm_approvals=task.pm_approvals or {}, team=task.team, created_by=require_uuid(task.created_by), assigned_to=to_python_uuid(task.assigned_to), diff --git a/roboco/db/tables.py b/roboco/db/tables.py index ab61265a..cd50c47e 100644 --- a/roboco/db/tables.py +++ b/roboco/db/tables.py @@ -196,9 +196,6 @@ class TaskTable(Base): docs_complete: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) pr_created: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False) - # PM Approval Tracking (for AWAITING_PM_REVIEW phase) - pm_approvals: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict) - # Ownership created_by: Mapped[UUID] = mapped_column( UUID(as_uuid=True), ForeignKey("agents.id"), nullable=False diff --git a/roboco/models/task.py b/roboco/models/task.py index 2dfc428a..28308e02 100644 --- a/roboco/models/task.py +++ b/roboco/models/task.py @@ -190,12 +190,6 @@ class Task(TimestampMixin): docs_complete: bool = Field(default=False, description="Documenter has finished") pr_created: bool = Field(default=False, description="Developer has created PR") - # PM Approval Tracking (for AWAITING_PM_REVIEW phase) - pm_approvals: dict[str, bool] = Field( - default_factory=dict, - description="PM approvals: {'main_pm': True, 'cell_pm': True}", - ) - # Ownership created_by: UUID = Field(..., description="Agent who created the task") assigned_to: UUID | None = Field( @@ -370,7 +364,6 @@ class TaskUpdate(RobocoBase): pr_url: str | None = None docs_complete: bool | None = None pr_created: bool | None = None - pm_approvals: dict[str, bool] | None = None # ============================================================================= diff --git a/tests/conftest.py b/tests/conftest.py index cd1d585e..b04568a9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -346,7 +346,6 @@ async def smoke_test_batch(db_session: AsyncSession) -> AsyncIterator[list[UUID] pr_url="https://github.com/example/smoke/pull/1", docs_complete=True, pr_created=True, - pm_approvals={"cell_pm": str(pm_agent.id)}, created_by=system_agent.id, assigned_to=dev_agent.id, team=Team.BACKEND, diff --git a/tests/integration/test_migration_014_drop_pm_approvals.py b/tests/integration/test_migration_014_drop_pm_approvals.py new file mode 100644 index 00000000..96f01af4 --- /dev/null +++ b/tests/integration/test_migration_014_drop_pm_approvals.py @@ -0,0 +1,41 @@ +"""Wave B2 (2026-05-12, re-scoped): migration 014 drops pm_approvals. + +Original spec proposed dropping three Task columns; investigation found +quick_context and proactive_context are actively used (original_developer +tracking, RAG context). Only pm_approvals is truly orphaned. +""" + +from __future__ import annotations + +import pytest +from sqlalchemy import text + + +@pytest.mark.asyncio +async def test_pm_approvals_dropped(db_session) -> None: # type: ignore[no-untyped-def] + """pm_approvals column is gone from the tasks table.""" + result = await db_session.execute( + text( + "SELECT column_name FROM information_schema.columns " + "WHERE table_name = 'tasks' AND column_name = 'pm_approvals'" + ) + ) + rows = list(result) + assert rows == [], "pm_approvals column should have been dropped" + + +@pytest.mark.asyncio +async def test_quick_context_and_proactive_context_remain(db_session) -> None: # type: ignore[no-untyped-def] + """quick_context and proactive_context MUST remain — they're actively used.""" + result = await db_session.execute( + text( + "SELECT column_name FROM information_schema.columns " + "WHERE table_name = 'tasks' " + "AND column_name IN ('quick_context', 'proactive_context')" + ) + ) + rows = {r[0] for r in result} + assert "quick_context" in rows, ( + "quick_context must remain (original_developer + audit)" + ) + assert "proactive_context" in rows, "proactive_context must remain (RAG injection)" diff --git a/tests/unit/api/test_schemas_tasks.py b/tests/unit/api/test_schemas_tasks.py index 371f6fb6..9e45137e 100644 --- a/tests/unit/api/test_schemas_tasks.py +++ b/tests/unit/api/test_schemas_tasks.py @@ -276,7 +276,6 @@ def _stub_task(*, with_project: bool = False) -> SimpleNamespace: project=(SimpleNamespace(slug="proj-1") if with_project else None), docs_complete=False, pr_created=False, - pm_approvals={}, team=Team.BACKEND, created_by=uuid4(), assigned_to=None, diff --git a/tests/unit/services/test_audit_agent_id.py b/tests/unit/services/test_audit_agent_id.py index 1ce91d2b..fef57491 100644 --- a/tests/unit/services/test_audit_agent_id.py +++ b/tests/unit/services/test_audit_agent_id.py @@ -259,7 +259,6 @@ async def test_submit_for_qa_writes_audit_with_dev_agent_id( pr_url="https://github.com/example/audit-test/pull/99", docs_complete=False, pr_created=True, - pm_approvals={}, created_by=system_uuid, assigned_to=dev_uuid, claimed_by=dev_uuid,