Skip to content

Commit 8c1f975

Browse files
committed
feat(agent): add review queue, versioning, bundles, archive, and draft workflow
- Fix migration 0010 variable shadowing (inner_row) - Add migration 0011: AgentStatus pending/rejected, ListingStatus archived, component_bundles table, bundle_id FK on all listing tables, rejection_reason - Add semver utilities (parse, validate, bump, suggest) with full test coverage - Add agent review routes: list pending agents with component readiness check, approve (gated on all components approved), reject with reason - Add bundle review: atomic approve/reject all components in a bundle - Add agent archive endpoint (soft delete, admin only) - Add draft workflow: save draft, update draft, submit for review - Add version-suggestions endpoint and version bump on update - Add created_by_email to agent list, detail, and leaderboard responses - Add component leaderboard endpoint with user filter - Add interactive CLI search (-i flag) for agents and MCP servers - CLI: version bump prompt on publish --update, --draft flag, --beta for init - CLI: agent delete now archives instead of hard-deleting - Create GitHub issue #339 for username field Signed-off-by: Hari Srinivasan <harisrini21@gmail.com>
1 parent 9d59346 commit 8c1f975

File tree

21 files changed

+889
-46
lines changed

21 files changed

+889
-46
lines changed

observal-server/alembic/versions/0010_add_agents_name_uniqueness.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,23 @@ def upgrade() -> None:
2222
DO $$
2323
DECLARE
2424
dup_row RECORD;
25+
inner_row RECORD;
2526
counter INT;
2627
BEGIN
2728
FOR dup_row IN
2829
SELECT name, created_by FROM agents
2930
GROUP BY name, created_by HAVING COUNT(*) > 1
3031
LOOP
3132
counter := 0;
32-
FOR dup_row IN
33+
FOR inner_row IN
3334
SELECT id FROM agents
3435
WHERE name = dup_row.name AND created_by = dup_row.created_by
3536
ORDER BY created_at DESC OFFSET 1
3637
LOOP
3738
counter := counter + 1;
3839
UPDATE agents
3940
SET name = name || '-dup' || counter
40-
WHERE id = dup_row.id;
41+
WHERE id = inner_row.id;
4142
END LOOP;
4243
END LOOP;
4344
END
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""Add agent review statuses, component bundles, and archive support.
2+
3+
- Extend AgentStatus enum with 'pending' and 'rejected'
4+
- Extend ListingStatus enum with 'archived'
5+
- Create component_bundles table for MCP-linked skills
6+
- Add bundle_id FK to all 5 listing tables
7+
- Add rejection_reason column to agents
8+
9+
Revision ID: 0011
10+
Revises: 0010
11+
Create Date: 2026-04-17
12+
"""
13+
14+
from alembic import op
15+
import sqlalchemy as sa
16+
from sqlalchemy.dialects.postgresql import UUID
17+
18+
revision = "0011"
19+
down_revision = "0010"
20+
branch_labels = None
21+
depends_on = None
22+
23+
_LISTING_TABLES = [
24+
"mcp_listings",
25+
"skill_listings",
26+
"hook_listings",
27+
"prompt_listings",
28+
"sandbox_listings",
29+
]
30+
31+
32+
def upgrade() -> None:
33+
# Enum extensions must run outside a transaction in PostgreSQL
34+
op.execute("COMMIT")
35+
op.execute("ALTER TYPE agentstatus ADD VALUE IF NOT EXISTS 'pending'")
36+
op.execute("ALTER TYPE agentstatus ADD VALUE IF NOT EXISTS 'rejected'")
37+
op.execute("ALTER TYPE listingstatus ADD VALUE IF NOT EXISTS 'archived'")
38+
op.execute("BEGIN")
39+
40+
# Component bundles table
41+
op.execute("""
42+
CREATE TABLE IF NOT EXISTS component_bundles (
43+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
44+
name VARCHAR(255) NOT NULL,
45+
description TEXT DEFAULT '',
46+
submitted_by UUID NOT NULL REFERENCES users(id),
47+
created_at TIMESTAMPTZ DEFAULT NOW()
48+
)
49+
""")
50+
51+
# Add bundle_id FK + index to each listing table
52+
for table in _LISTING_TABLES:
53+
op.execute(f"""
54+
DO $$
55+
BEGIN
56+
IF NOT EXISTS (
57+
SELECT 1 FROM information_schema.columns
58+
WHERE table_name = '{table}' AND column_name = 'bundle_id'
59+
) THEN
60+
ALTER TABLE {table}
61+
ADD COLUMN bundle_id UUID REFERENCES component_bundles(id);
62+
END IF;
63+
END $$;
64+
""")
65+
op.execute(f"""
66+
CREATE INDEX IF NOT EXISTS ix_{table}_bundle_id ON {table}(bundle_id)
67+
""")
68+
69+
# Add rejection_reason to agents
70+
op.execute("""
71+
DO $$
72+
BEGIN
73+
IF NOT EXISTS (
74+
SELECT 1 FROM information_schema.columns
75+
WHERE table_name = 'agents' AND column_name = 'rejection_reason'
76+
) THEN
77+
ALTER TABLE agents ADD COLUMN rejection_reason TEXT;
78+
END IF;
79+
END $$;
80+
""")
81+
82+
# Index on agents.status for review queries
83+
op.execute("CREATE INDEX IF NOT EXISTS ix_agents_status ON agents(status)")
84+
85+
86+
def downgrade() -> None:
87+
op.execute("DROP INDEX IF EXISTS ix_agents_status")
88+
op.execute("ALTER TABLE agents DROP COLUMN IF EXISTS rejection_reason")
89+
for table in _LISTING_TABLES:
90+
op.execute(f"DROP INDEX IF EXISTS ix_{table}_bundle_id")
91+
op.execute(f"ALTER TABLE {table} DROP COLUMN IF EXISTS bundle_id")
92+
op.execute("DROP TABLE IF EXISTS component_bundles")

0 commit comments

Comments
 (0)