mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
Two real bugs surfaced running the full gate against a containerized
Postgres (and the orchestrator boot log):
1. Migration 052 crashed a real orchestrator boot with
'type "team" already exists'. The generic sa.Enum(create_type=False)
does NOT set the postgres enum's create_type attribute, so op.create_table
(checkfirst=False) emitted a redundant CREATE TYPE against the pre-existing
team enum. Switched to postgresql.ENUM(create_type=False) — the postgres-
native enum whose create_type _check_for_name_in_memos actually reads, so
the CREATE TYPE is suppressed. Verified: 051->052 upgrade against a DB where
the team enum pre-existed (the exact path that crashed) now succeeds;
downgrade 052->051 drops the table and preserves the shared enum; fresh
upgrade head clean. (Migration 016 has the same latent sa.Enum pattern but
never re-runs in prod, so it's noted, not touched here.)
2. _ensure_branch_for_task read task.cell_projects (lazy=selectin to-many)
directly, tripping MissingGreenlet on a freshly-created/unqueried task —
which then poisoned the async session (PendingRollbackError). Replaced with
_task_has_cell_map: peeks InstanceState.unloaded (no IO) and reads the
already-loaded map, falling back to an awaited count query only when the
relationship is genuinely unloaded. Non-ORM stubs route to the plain
attribute. Fixes 2 integration tests; the 6 cell-map unit tests still pass.
Also: typed the self stub as Any in test_choreographer_subtask_project
(mypy tests/ wants Choreographer, not SimpleNamespace) — the codebase idiom.
Gate: ruff format/check clean; mypy roboco/ + tests/ clean; full pytest
10371 passed / 388 skipped against containerized pgvector:pg16; vulture clean.
Pre-existing xenon C-rank on reassign (from prior commit 19a474d3, not this
feature) still blocks make quality — surfaced separately.
73 lines
2.5 KiB
Python
73 lines
2.5 KiB
Python
"""Add the task_cell_projects table — ad-hoc per-cell project map for a task.
|
|
|
|
A MegaTask root-subtask that spans multiple cells (and may mix per-cell projects
|
|
from different products / OSS libs) needs a per-cell routing map without standing
|
|
up a Product for it. ``task_cell_projects`` mirrors ``product_projects`` but is
|
|
owned by the task: one Project per cell per task (``UNIQUE (task_id, team)``). The
|
|
root-subtask then cuts ``feature/main_pm/{root}`` per repo and opens a root->master
|
|
PR per repo exactly like a Product fan-out root — only the map's source differs.
|
|
``team`` reuses the existing Postgres "team" enum (create_type=False).
|
|
|
|
Revision ID: 052_task_cell_projects
|
|
Revises: 051_respawn_tracker
|
|
Create Date: 2026-06-26
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
revision = "052_task_cell_projects"
|
|
down_revision = "051_respawn_tracker"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
# Reuse the existing Postgres "team" enum in place (created in 001_initial_schema,
|
|
# widened since by later migrations). ``create_type=False`` MUST be set on the
|
|
# postgres-native ``postgresql.ENUM``: it's that class's ``create_type`` attribute
|
|
# that ``_check_for_name_in_memos`` reads to suppress the redundant ``CREATE TYPE``
|
|
# on ``op.create_table`` (checkfirst=False, so the has_type probe is skipped). On
|
|
# the generic ``sa.Enum`` the kwarg is silently dropped, so the CREATE TYPE fires
|
|
# and crashes a real orchestrator boot ("type 'team' already exists").
|
|
_TEAM_ENUM = postgresql.ENUM(
|
|
"backend",
|
|
"frontend",
|
|
"ux_ui",
|
|
"board",
|
|
"main_pm",
|
|
"fullstack",
|
|
"marketing",
|
|
"system",
|
|
name="team",
|
|
create_type=False,
|
|
)
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"task_cell_projects",
|
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
|
sa.Column(
|
|
"task_id",
|
|
postgresql.UUID(as_uuid=True),
|
|
sa.ForeignKey("tasks.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.Column("team", _TEAM_ENUM, nullable=False),
|
|
sa.Column(
|
|
"project_id",
|
|
postgresql.UUID(as_uuid=True),
|
|
sa.ForeignKey("projects.id", ondelete="RESTRICT"),
|
|
nullable=False,
|
|
index=True,
|
|
),
|
|
sa.UniqueConstraint("task_id", "team", name="uq_task_cell_projects_task_team"),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_table("task_cell_projects")
|