Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
def upgrade() -> None:
op.execute("ALTER TYPE userrole ADD VALUE IF NOT EXISTS 'super_admin'")
op.execute("ALTER TYPE userrole ADD VALUE IF NOT EXISTS 'reviewer'")
# New enum values must be committed before they can be used in DML
op.execute("COMMIT")
# Only rename developer→reviewer if the enum still has the old value
op.execute("""
DO $$
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Add unique constraint on agents (name, created_by).

Same user cannot create two agents with the same name; different users can.

Revision ID: 0010
Revises: 0009
Create Date: 2026-04-17
"""

from alembic import op

revision = "0010"
down_revision = "0009"
branch_labels = None
depends_on = None


def upgrade() -> None:
# Deduplicate any existing duplicates first: keep the most recent row,
# rename older duplicates by appending a suffix so the index can be created.
op.execute("""
DO $$
DECLARE
dup_row RECORD;
inner_row RECORD;
counter INT;
BEGIN
FOR dup_row IN
SELECT name, created_by FROM agents
GROUP BY name, created_by HAVING COUNT(*) > 1
LOOP
counter := 0;
FOR inner_row IN
SELECT id FROM agents
WHERE name = dup_row.name AND created_by = dup_row.created_by
ORDER BY created_at DESC OFFSET 1
LOOP
counter := counter + 1;
UPDATE agents
SET name = name || '-dup' || counter
WHERE id = inner_row.id;
END LOOP;
END LOOP;
END
$$;
""")
op.execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'uq_agents_name_created_by'
) THEN
ALTER TABLE agents
ADD CONSTRAINT uq_agents_name_created_by UNIQUE (name, created_by);
END IF;
END
$$;
""")


def downgrade() -> None:
op.execute("ALTER TABLE agents DROP CONSTRAINT IF EXISTS uq_agents_name_created_by;")
90 changes: 90 additions & 0 deletions observal-server/alembic/versions/0011_agent_review_and_bundles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""Add agent review statuses, component bundles, and archive support.

- Extend AgentStatus enum with 'pending' and 'rejected'
- Extend ListingStatus enum with 'archived'
- Create component_bundles table for MCP-linked skills
- Add bundle_id FK to all 5 listing tables
- Add rejection_reason column to agents

Revision ID: 0011
Revises: 0010
Create Date: 2026-04-17
"""

from alembic import op

revision = "0011"
down_revision = "0010"
branch_labels = None
depends_on = None

_LISTING_TABLES = [
"mcp_listings",
"skill_listings",
"hook_listings",
"prompt_listings",
"sandbox_listings",
]


def upgrade() -> None:
# Enum extensions must run outside a transaction in PostgreSQL
op.execute("COMMIT")
op.execute("ALTER TYPE agentstatus ADD VALUE IF NOT EXISTS 'pending'")
op.execute("ALTER TYPE agentstatus ADD VALUE IF NOT EXISTS 'rejected'")
op.execute("ALTER TYPE listingstatus ADD VALUE IF NOT EXISTS 'archived'")
op.execute("BEGIN")

# Component bundles table
op.execute("""
CREATE TABLE IF NOT EXISTS component_bundles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(255) NOT NULL,
description TEXT DEFAULT '',
submitted_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ DEFAULT NOW()
)
""")

# Add bundle_id FK + index to each listing table
for table in _LISTING_TABLES:
op.execute(f"""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = '{table}' AND column_name = 'bundle_id'
) THEN
ALTER TABLE {table}
ADD COLUMN bundle_id UUID REFERENCES component_bundles(id);
END IF;
END $$;
""")
op.execute(f"""
CREATE INDEX IF NOT EXISTS ix_{table}_bundle_id ON {table}(bundle_id)
""")

# Add rejection_reason to agents
op.execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'agents' AND column_name = 'rejection_reason'
) THEN
ALTER TABLE agents ADD COLUMN rejection_reason TEXT;
END IF;
END $$;
""")

# Index on agents.status for review queries
op.execute("CREATE INDEX IF NOT EXISTS ix_agents_status ON agents(status)")


def downgrade() -> None:
op.execute("DROP INDEX IF EXISTS ix_agents_status")
op.execute("ALTER TABLE agents DROP COLUMN IF EXISTS rejection_reason")
for table in _LISTING_TABLES:
op.execute(f"DROP INDEX IF EXISTS ix_{table}_bundle_id")
op.execute(f"ALTER TABLE {table} DROP COLUMN IF EXISTS bundle_id")
op.execute("DROP TABLE IF EXISTS component_bundles")
Loading
Loading