draft-board 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/backend/.env.example +9 -0
- package/app/backend/.smartkanban/evidence/8b383839-cbec-45af-86ee-c7708d075cbe/bddf2ed5-2e21-4d46-a62b-10b87f1642a6_patch.txt +195 -0
- package/app/backend/.smartkanban/evidence/8b383839-cbec-45af-86ee-c7708d075cbe/bddf2ed5-2e21-4d46-a62b-10b87f1642a6_stat.txt +6 -0
- package/app/backend/CURL_EXAMPLES.md +335 -0
- package/app/backend/ENV_SETUP.md +65 -0
- package/app/backend/alembic/env.py +71 -0
- package/app/backend/alembic/script.py.mako +28 -0
- package/app/backend/alembic/versions/001_initial_schema.py +104 -0
- package/app/backend/alembic/versions/002_add_jobs_table.py +52 -0
- package/app/backend/alembic/versions/003_add_workspace_table.py +48 -0
- package/app/backend/alembic/versions/004_add_evidence_table.py +56 -0
- package/app/backend/alembic/versions/005_add_verification_commands.py +32 -0
- package/app/backend/alembic/versions/006_add_planner_lock_table.py +39 -0
- package/app/backend/alembic/versions/007_add_revision_review_tables.py +126 -0
- package/app/backend/alembic/versions/008_add_revision_idempotency_and_traceability.py +52 -0
- package/app/backend/alembic/versions/009_add_job_health_fields.py +46 -0
- package/app/backend/alembic/versions/010_add_review_comment_line_content.py +36 -0
- package/app/backend/alembic/versions/011_add_analysis_cache.py +47 -0
- package/app/backend/alembic/versions/012_add_boards_table.py +102 -0
- package/app/backend/alembic/versions/013_add_ticket_blocking.py +45 -0
- package/app/backend/alembic/versions/014_add_agent_sessions.py +220 -0
- package/app/backend/alembic/versions/015_add_ticket_sort_order.py +33 -0
- package/app/backend/alembic/versions/03220f0b93ae_add_pr_fields_to_ticket.py +49 -0
- package/app/backend/alembic/versions/0c2d89fff3b1_seed_board_configs_from_yaml.py +206 -0
- package/app/backend/alembic/versions/3348e5cf54c1_add_merge_checklist_table.py +67 -0
- package/app/backend/alembic/versions/357c780ee445_add_goal_status.py +34 -0
- package/app/backend/alembic/versions/553340b7e26c_add_autonomy_fields_to_goal.py +65 -0
- package/app/backend/alembic/versions/774dc335c679_merge_migration_heads.py +23 -0
- package/app/backend/alembic/versions/7b307e847cbd_merge_heads.py +23 -0
- package/app/backend/alembic/versions/82ecd978cc70_add_missing_indexes.py +48 -0
- package/app/backend/alembic/versions/8ef5054dc280_add_normalized_log_entries.py +173 -0
- package/app/backend/alembic/versions/8f3e2bd8ea3b_merge_migration_heads.py +23 -0
- package/app/backend/alembic/versions/9d17f0698d3b_add_config_column_to_boards_table.py +30 -0
- package/app/backend/alembic/versions/add_agent_conversation_history.py +72 -0
- package/app/backend/alembic/versions/add_job_variant.py +34 -0
- package/app/backend/alembic/versions/add_performance_indexes.py +95 -0
- package/app/backend/alembic/versions/add_repos_and_board_repos.py +174 -0
- package/app/backend/alembic/versions/add_session_id_to_jobs.py +27 -0
- package/app/backend/alembic/versions/add_sqlite_backend_tables.py +104 -0
- package/app/backend/alembic/versions/b10fb0b62240_add_diff_content_to_revisions.py +34 -0
- package/app/backend/alembic.ini +89 -0
- package/app/backend/app/__init__.py +3 -0
- package/app/backend/app/data_dir.py +85 -0
- package/app/backend/app/database.py +70 -0
- package/app/backend/app/database_sync.py +64 -0
- package/app/backend/app/dependencies/__init__.py +5 -0
- package/app/backend/app/dependencies/auth.py +80 -0
- package/app/backend/app/dependencies.py +43 -0
- package/app/backend/app/exceptions.py +178 -0
- package/app/backend/app/executors/__init__.py +1 -0
- package/app/backend/app/executors/adapters/__init__.py +1 -0
- package/app/backend/app/executors/adapters/aider.py +152 -0
- package/app/backend/app/executors/adapters/amazon_q.py +103 -0
- package/app/backend/app/executors/adapters/amp.py +123 -0
- package/app/backend/app/executors/adapters/claude.py +177 -0
- package/app/backend/app/executors/adapters/cline.py +127 -0
- package/app/backend/app/executors/adapters/codex.py +167 -0
- package/app/backend/app/executors/adapters/copilot.py +202 -0
- package/app/backend/app/executors/adapters/cursor.py +87 -0
- package/app/backend/app/executors/adapters/droid.py +123 -0
- package/app/backend/app/executors/adapters/gemini.py +132 -0
- package/app/backend/app/executors/adapters/goose.py +131 -0
- package/app/backend/app/executors/adapters/opencode.py +123 -0
- package/app/backend/app/executors/adapters/qwen.py +123 -0
- package/app/backend/app/executors/plugins/__init__.py +1 -0
- package/app/backend/app/executors/registry.py +202 -0
- package/app/backend/app/executors/spec.py +226 -0
- package/app/backend/app/main.py +486 -0
- package/app/backend/app/middleware/__init__.py +13 -0
- package/app/backend/app/middleware/idempotency.py +426 -0
- package/app/backend/app/middleware/rate_limit.py +312 -0
- package/app/backend/app/middleware/security_headers.py +43 -0
- package/app/backend/app/middleware/timeout.py +37 -0
- package/app/backend/app/models/__init__.py +56 -0
- package/app/backend/app/models/agent_conversation_history.py +56 -0
- package/app/backend/app/models/agent_session.py +127 -0
- package/app/backend/app/models/analysis_cache.py +49 -0
- package/app/backend/app/models/base.py +9 -0
- package/app/backend/app/models/board.py +79 -0
- package/app/backend/app/models/board_repo.py +68 -0
- package/app/backend/app/models/cost_budget.py +42 -0
- package/app/backend/app/models/enums.py +40 -0
- package/app/backend/app/models/evidence.py +132 -0
- package/app/backend/app/models/goal.py +102 -0
- package/app/backend/app/models/idempotency_entry.py +30 -0
- package/app/backend/app/models/job.py +163 -0
- package/app/backend/app/models/job_queue.py +39 -0
- package/app/backend/app/models/kv_store.py +28 -0
- package/app/backend/app/models/merge_checklist.py +87 -0
- package/app/backend/app/models/normalized_log.py +100 -0
- package/app/backend/app/models/planner_lock.py +43 -0
- package/app/backend/app/models/rate_limit_entry.py +25 -0
- package/app/backend/app/models/repo.py +66 -0
- package/app/backend/app/models/review_comment.py +91 -0
- package/app/backend/app/models/review_summary.py +69 -0
- package/app/backend/app/models/revision.py +130 -0
- package/app/backend/app/models/ticket.py +223 -0
- package/app/backend/app/models/ticket_event.py +83 -0
- package/app/backend/app/models/user.py +47 -0
- package/app/backend/app/models/workspace.py +71 -0
- package/app/backend/app/redis_client.py +119 -0
- package/app/backend/app/routers/__init__.py +29 -0
- package/app/backend/app/routers/agents.py +296 -0
- package/app/backend/app/routers/auth.py +94 -0
- package/app/backend/app/routers/board.py +885 -0
- package/app/backend/app/routers/dashboard.py +351 -0
- package/app/backend/app/routers/debug.py +528 -0
- package/app/backend/app/routers/evidence.py +96 -0
- package/app/backend/app/routers/executors.py +324 -0
- package/app/backend/app/routers/goals.py +574 -0
- package/app/backend/app/routers/jobs.py +448 -0
- package/app/backend/app/routers/maintenance.py +172 -0
- package/app/backend/app/routers/merge.py +360 -0
- package/app/backend/app/routers/planner.py +537 -0
- package/app/backend/app/routers/pull_requests.py +382 -0
- package/app/backend/app/routers/repos.py +263 -0
- package/app/backend/app/routers/revisions.py +939 -0
- package/app/backend/app/routers/settings.py +267 -0
- package/app/backend/app/routers/tickets.py +2003 -0
- package/app/backend/app/routers/webhooks.py +143 -0
- package/app/backend/app/routers/websocket.py +249 -0
- package/app/backend/app/schemas/__init__.py +109 -0
- package/app/backend/app/schemas/board.py +87 -0
- package/app/backend/app/schemas/common.py +33 -0
- package/app/backend/app/schemas/evidence.py +87 -0
- package/app/backend/app/schemas/goal.py +90 -0
- package/app/backend/app/schemas/job.py +97 -0
- package/app/backend/app/schemas/merge.py +139 -0
- package/app/backend/app/schemas/planner.py +500 -0
- package/app/backend/app/schemas/repo.py +187 -0
- package/app/backend/app/schemas/review.py +137 -0
- package/app/backend/app/schemas/revision.py +114 -0
- package/app/backend/app/schemas/ticket.py +238 -0
- package/app/backend/app/schemas/ticket_event.py +72 -0
- package/app/backend/app/schemas/workspace.py +19 -0
- package/app/backend/app/services/__init__.py +31 -0
- package/app/backend/app/services/agent_memory_service.py +223 -0
- package/app/backend/app/services/agent_registry.py +346 -0
- package/app/backend/app/services/agent_session_manager.py +318 -0
- package/app/backend/app/services/agent_session_service.py +219 -0
- package/app/backend/app/services/agent_tools.py +379 -0
- package/app/backend/app/services/auth_service.py +98 -0
- package/app/backend/app/services/autonomy_service.py +380 -0
- package/app/backend/app/services/board_repo_service.py +201 -0
- package/app/backend/app/services/board_service.py +326 -0
- package/app/backend/app/services/cleanup_service.py +1085 -0
- package/app/backend/app/services/config_service.py +908 -0
- package/app/backend/app/services/context_gatherer.py +557 -0
- package/app/backend/app/services/cost_tracking_service.py +293 -0
- package/app/backend/app/services/cursor_log_normalizer.py +536 -0
- package/app/backend/app/services/delivery_pipeline.py +440 -0
- package/app/backend/app/services/executor_service.py +634 -0
- package/app/backend/app/services/git_host/__init__.py +11 -0
- package/app/backend/app/services/git_host/factory.py +87 -0
- package/app/backend/app/services/git_host/github.py +270 -0
- package/app/backend/app/services/git_host/gitlab.py +194 -0
- package/app/backend/app/services/git_host/protocol.py +75 -0
- package/app/backend/app/services/git_merge_simple.py +346 -0
- package/app/backend/app/services/git_ops.py +384 -0
- package/app/backend/app/services/github_service.py +233 -0
- package/app/backend/app/services/goal_service.py +113 -0
- package/app/backend/app/services/job_service.py +423 -0
- package/app/backend/app/services/job_watchdog_service.py +424 -0
- package/app/backend/app/services/langchain_adapter.py +122 -0
- package/app/backend/app/services/llm_provider_clients.py +351 -0
- package/app/backend/app/services/llm_service.py +285 -0
- package/app/backend/app/services/log_normalizer.py +342 -0
- package/app/backend/app/services/log_stream_service.py +276 -0
- package/app/backend/app/services/merge_checklist_service.py +264 -0
- package/app/backend/app/services/merge_service.py +784 -0
- package/app/backend/app/services/orchestrator_log.py +84 -0
- package/app/backend/app/services/planner_service.py +1662 -0
- package/app/backend/app/services/planner_tick_sync.py +1040 -0
- package/app/backend/app/services/queued_message_service.py +156 -0
- package/app/backend/app/services/reliability_wrapper.py +389 -0
- package/app/backend/app/services/repo_discovery_service.py +318 -0
- package/app/backend/app/services/review_service.py +334 -0
- package/app/backend/app/services/revision_service.py +389 -0
- package/app/backend/app/services/safe_autopilot.py +510 -0
- package/app/backend/app/services/sqlite_worker.py +372 -0
- package/app/backend/app/services/task_dispatch.py +135 -0
- package/app/backend/app/services/ticket_generation_service.py +1781 -0
- package/app/backend/app/services/ticket_service.py +486 -0
- package/app/backend/app/services/udar_planner_service.py +1007 -0
- package/app/backend/app/services/webhook_service.py +126 -0
- package/app/backend/app/services/workspace_service.py +465 -0
- package/app/backend/app/services/worktree_file_service.py +92 -0
- package/app/backend/app/services/worktree_validator.py +213 -0
- package/app/backend/app/sqlite_kv.py +278 -0
- package/app/backend/app/state_machine.py +128 -0
- package/app/backend/app/templates/__init__.py +5 -0
- package/app/backend/app/templates/registry.py +243 -0
- package/app/backend/app/utils/__init__.py +5 -0
- package/app/backend/app/utils/artifact_reader.py +87 -0
- package/app/backend/app/utils/circuit_breaker.py +229 -0
- package/app/backend/app/utils/db_retry.py +136 -0
- package/app/backend/app/utils/ignored_fields.py +123 -0
- package/app/backend/app/utils/validators.py +54 -0
- package/app/backend/app/websocket/__init__.py +5 -0
- package/app/backend/app/websocket/manager.py +179 -0
- package/app/backend/app/websocket/state_tracker.py +113 -0
- package/app/backend/app/worker.py +3190 -0
- package/app/backend/calculator_tickets.json +40 -0
- package/app/backend/canary_tests.sh +591 -0
- package/app/backend/celerybeat-schedule +0 -0
- package/app/backend/celerybeat-schedule-shm +0 -0
- package/app/backend/celerybeat-schedule-wal +0 -0
- package/app/backend/logs/.gitkeep +3 -0
- package/app/backend/multiplication_division_implementation_tickets.json +55 -0
- package/app/backend/multiplication_division_tickets.json +42 -0
- package/app/backend/pyproject.toml +45 -0
- package/app/backend/requirements-dev.txt +8 -0
- package/app/backend/requirements.txt +20 -0
- package/app/backend/run.sh +30 -0
- package/app/backend/run_with_logs.sh +10 -0
- package/app/backend/scientific_calculator_tickets.json +40 -0
- package/app/backend/scripts/extract_openapi.py +21 -0
- package/app/backend/scripts/seed_demo.py +187 -0
- package/app/backend/setup_demo_review.py +302 -0
- package/app/backend/test_actual_parse.py +41 -0
- package/app/backend/test_agent_streaming.py +61 -0
- package/app/backend/test_parse.py +51 -0
- package/app/backend/test_streaming.py +51 -0
- package/app/backend/test_subprocess_streaming.py +50 -0
- package/app/backend/tests/__init__.py +1 -0
- package/app/backend/tests/conftest.py +46 -0
- package/app/backend/tests/test_auth.py +341 -0
- package/app/backend/tests/test_autonomy_service.py +391 -0
- package/app/backend/tests/test_cleanup_service_safety.py +417 -0
- package/app/backend/tests/test_middleware.py +279 -0
- package/app/backend/tests/test_planner_providers.py +290 -0
- package/app/backend/tests/test_planner_unblock.py +183 -0
- package/app/backend/tests/test_revision_invariants.py +618 -0
- package/app/backend/tests/test_sqlite_kv.py +290 -0
- package/app/backend/tests/test_sqlite_worker.py +353 -0
- package/app/backend/tests/test_task_dispatch.py +100 -0
- package/app/backend/tests/test_ticket_validation.py +304 -0
- package/app/backend/tests/test_udar_agent.py +693 -0
- package/app/backend/tests/test_webhook_service.py +184 -0
- package/app/backend/tickets_output.json +59 -0
- package/app/backend/user_management_tickets.json +50 -0
- package/app/backend/uvicorn.log +0 -0
- package/app/draft.yaml +313 -0
- package/app/frontend/dist/assets/index-LcjCczu5.js +155 -0
- package/app/frontend/dist/assets/index-_FP_279e.css +1 -0
- package/app/frontend/dist/index.html +14 -0
- package/app/frontend/dist/vite.svg +1 -0
- package/app/frontend/package.json +101 -0
- package/bin/cli.js +527 -0
- package/package.json +37 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""add_merge_checklist_table
|
|
2
|
+
|
|
3
|
+
Revision ID: 3348e5cf54c1
|
|
4
|
+
Revises: 8f3e2bd8ea3b
|
|
5
|
+
Create Date: 2026-01-29 12:40:59.627310
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "3348e5cf54c1"
|
|
17
|
+
down_revision: str | None = "8f3e2bd8ea3b"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
# Create merge_checklists table
|
|
24
|
+
op.create_table(
|
|
25
|
+
"merge_checklists",
|
|
26
|
+
sa.Column("id", sa.String(length=36), nullable=False),
|
|
27
|
+
sa.Column("goal_id", sa.String(length=36), nullable=False),
|
|
28
|
+
sa.Column("all_tests_passed", sa.Boolean(), nullable=False),
|
|
29
|
+
sa.Column("total_files_changed", sa.Integer(), nullable=False),
|
|
30
|
+
sa.Column("total_lines_changed", sa.Integer(), nullable=False),
|
|
31
|
+
sa.Column("total_cost_usd", sa.Float(), nullable=True),
|
|
32
|
+
sa.Column("budget_exceeded", sa.Boolean(), nullable=False),
|
|
33
|
+
sa.Column("code_reviewed", sa.Boolean(), nullable=False),
|
|
34
|
+
sa.Column("no_sensitive_data", sa.Boolean(), nullable=False),
|
|
35
|
+
sa.Column("rollback_plan_understood", sa.Boolean(), nullable=False),
|
|
36
|
+
sa.Column("documentation_updated", sa.Boolean(), nullable=False),
|
|
37
|
+
sa.Column("rollback_plan_json", sa.Text(), nullable=True),
|
|
38
|
+
sa.Column("risk_level", sa.String(length=20), nullable=False),
|
|
39
|
+
sa.Column("ready_to_merge", sa.Boolean(), nullable=False),
|
|
40
|
+
sa.Column("merged_at", sa.DateTime(), nullable=True),
|
|
41
|
+
sa.Column(
|
|
42
|
+
"created_at",
|
|
43
|
+
sa.DateTime(),
|
|
44
|
+
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
45
|
+
nullable=False,
|
|
46
|
+
),
|
|
47
|
+
sa.Column(
|
|
48
|
+
"updated_at",
|
|
49
|
+
sa.DateTime(),
|
|
50
|
+
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
51
|
+
nullable=False,
|
|
52
|
+
),
|
|
53
|
+
sa.ForeignKeyConstraint(["goal_id"], ["goals.id"], ondelete="CASCADE"),
|
|
54
|
+
sa.PrimaryKeyConstraint("id"),
|
|
55
|
+
)
|
|
56
|
+
op.create_index(
|
|
57
|
+
op.f("ix_merge_checklists_goal_id"),
|
|
58
|
+
"merge_checklists",
|
|
59
|
+
["goal_id"],
|
|
60
|
+
unique=False,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def downgrade() -> None:
|
|
65
|
+
# Drop merge_checklists table
|
|
66
|
+
op.drop_index(op.f("ix_merge_checklists_goal_id"), table_name="merge_checklists")
|
|
67
|
+
op.drop_table("merge_checklists")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""add_goal_status
|
|
2
|
+
|
|
3
|
+
Revision ID: 357c780ee445
|
|
4
|
+
Revises: add_job_variant_001
|
|
5
|
+
Create Date: 2026-01-29 19:58:09.271091
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "357c780ee445"
|
|
17
|
+
down_revision: str | None = "add_job_variant_001"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
# Add status column to goals table
|
|
24
|
+
op.add_column(
|
|
25
|
+
"goals",
|
|
26
|
+
sa.Column(
|
|
27
|
+
"status", sa.String(length=20), nullable=False, server_default="proposed"
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def downgrade() -> None:
|
|
33
|
+
# Remove status column from goals table
|
|
34
|
+
op.drop_column("goals", "status")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""add autonomy fields to goal
|
|
2
|
+
|
|
3
|
+
Revision ID: 553340b7e26c
|
|
4
|
+
Revises: 7b307e847cbd
|
|
5
|
+
Create Date: 2026-02-16 13:31:00.783226
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "553340b7e26c"
|
|
17
|
+
down_revision: str | None = "7b307e847cbd"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
op.add_column(
|
|
24
|
+
"goals",
|
|
25
|
+
sa.Column("autonomy_enabled", sa.Boolean(), server_default="0", nullable=False),
|
|
26
|
+
)
|
|
27
|
+
op.add_column(
|
|
28
|
+
"goals",
|
|
29
|
+
sa.Column(
|
|
30
|
+
"auto_approve_tickets", sa.Boolean(), server_default="0", nullable=False
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
op.add_column(
|
|
34
|
+
"goals",
|
|
35
|
+
sa.Column(
|
|
36
|
+
"auto_approve_revisions", sa.Boolean(), server_default="0", nullable=False
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
op.add_column(
|
|
40
|
+
"goals",
|
|
41
|
+
sa.Column("auto_merge", sa.Boolean(), server_default="0", nullable=False),
|
|
42
|
+
)
|
|
43
|
+
op.add_column(
|
|
44
|
+
"goals",
|
|
45
|
+
sa.Column(
|
|
46
|
+
"auto_approve_followups", sa.Boolean(), server_default="0", nullable=False
|
|
47
|
+
),
|
|
48
|
+
)
|
|
49
|
+
op.add_column("goals", sa.Column("max_auto_approvals", sa.Integer(), nullable=True))
|
|
50
|
+
op.add_column(
|
|
51
|
+
"goals",
|
|
52
|
+
sa.Column(
|
|
53
|
+
"auto_approval_count", sa.Integer(), server_default="0", nullable=False
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def downgrade() -> None:
|
|
59
|
+
op.drop_column("goals", "auto_approval_count")
|
|
60
|
+
op.drop_column("goals", "max_auto_approvals")
|
|
61
|
+
op.drop_column("goals", "auto_approve_followups")
|
|
62
|
+
op.drop_column("goals", "auto_merge")
|
|
63
|
+
op.drop_column("goals", "auto_approve_revisions")
|
|
64
|
+
op.drop_column("goals", "auto_approve_tickets")
|
|
65
|
+
op.drop_column("goals", "autonomy_enabled")
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""merge migration heads
|
|
2
|
+
|
|
3
|
+
Revision ID: 774dc335c679
|
|
4
|
+
Revises: 553340b7e26c, a1b2c3d4e5f6
|
|
5
|
+
Create Date: 2026-02-17 06:14:05.708067
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision: str = "774dc335c679"
|
|
13
|
+
down_revision: str | None = ("553340b7e26c", "a1b2c3d4e5f6")
|
|
14
|
+
branch_labels: str | Sequence[str] | None = None
|
|
15
|
+
depends_on: str | Sequence[str] | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade() -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def downgrade() -> None:
|
|
23
|
+
pass
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""merge heads
|
|
2
|
+
|
|
3
|
+
Revision ID: 7b307e847cbd
|
|
4
|
+
Revises: add_agent_conversation_history, c3a8f1b2d4e5
|
|
5
|
+
Create Date: 2026-02-16 13:23:30.635840
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision: str = "7b307e847cbd"
|
|
13
|
+
down_revision: str | None = ("add_agent_conversation_history", "c3a8f1b2d4e5")
|
|
14
|
+
branch_labels: str | Sequence[str] | None = None
|
|
15
|
+
depends_on: str | Sequence[str] | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade() -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def downgrade() -> None:
|
|
23
|
+
pass
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""add_missing_indexes
|
|
2
|
+
|
|
3
|
+
Revision ID: 82ecd978cc70
|
|
4
|
+
Revises: 774dc335c679
|
|
5
|
+
Create Date: 2026-03-01 06:04:37.402741
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from alembic import op
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision: str = "82ecd978cc70"
|
|
13
|
+
down_revision: str | None = "015"
|
|
14
|
+
branch_labels: str | None = None
|
|
15
|
+
depends_on: str | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade() -> None:
|
|
19
|
+
"""Add missing indexes for common query patterns.
|
|
20
|
+
|
|
21
|
+
Note: ix_goals_board_id and ix_revisions_job_id already exist
|
|
22
|
+
(created in migrations 012 and 007 respectively), so only the
|
|
23
|
+
composite indexes that are genuinely missing are added here.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Composite index for workspace cleanup queries
|
|
27
|
+
# (find active/stale workspaces ordered by creation)
|
|
28
|
+
op.create_index(
|
|
29
|
+
"ix_workspaces_cleanup",
|
|
30
|
+
"workspaces",
|
|
31
|
+
["cleaned_up_at", "created_at"],
|
|
32
|
+
unique=False,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Composite index for rate limit lookups
|
|
36
|
+
# (find entries by client_key that haven't expired)
|
|
37
|
+
op.create_index(
|
|
38
|
+
"ix_rate_limit_entries_lookup",
|
|
39
|
+
"rate_limit_entries",
|
|
40
|
+
["client_key", "expires_at"],
|
|
41
|
+
unique=False,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def downgrade() -> None:
|
|
46
|
+
"""Remove added indexes."""
|
|
47
|
+
op.drop_index("ix_rate_limit_entries_lookup", table_name="rate_limit_entries")
|
|
48
|
+
op.drop_index("ix_workspaces_cleanup", table_name="workspaces")
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""add_normalized_log_entries
|
|
2
|
+
|
|
3
|
+
Revision ID: 8ef5054dc280
|
|
4
|
+
Revises: 014
|
|
5
|
+
Create Date: 2026-01-12 10:47:40.188826
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "8ef5054dc280"
|
|
17
|
+
down_revision: str | None = "014"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
24
|
+
# Create normalized_log_entries table
|
|
25
|
+
op.create_table(
|
|
26
|
+
"normalized_log_entries",
|
|
27
|
+
sa.Column("id", sa.String(length=36), nullable=False),
|
|
28
|
+
sa.Column("job_id", sa.String(length=36), nullable=False),
|
|
29
|
+
sa.Column("sequence", sa.Integer(), nullable=False),
|
|
30
|
+
sa.Column("timestamp", sa.DateTime(), nullable=False),
|
|
31
|
+
sa.Column(
|
|
32
|
+
"entry_type",
|
|
33
|
+
sa.Enum(
|
|
34
|
+
"THINKING",
|
|
35
|
+
"FILE_EDIT",
|
|
36
|
+
"FILE_CREATE",
|
|
37
|
+
"FILE_DELETE",
|
|
38
|
+
"COMMAND_RUN",
|
|
39
|
+
"TOOL_CALL",
|
|
40
|
+
"ERROR",
|
|
41
|
+
"USER_MESSAGE",
|
|
42
|
+
"SYSTEM_MESSAGE",
|
|
43
|
+
"LOADING",
|
|
44
|
+
name="logentrytype",
|
|
45
|
+
),
|
|
46
|
+
nullable=False,
|
|
47
|
+
),
|
|
48
|
+
sa.Column("content", sa.Text(), nullable=False),
|
|
49
|
+
sa.Column("entry_metadata", sa.JSON(), nullable=True),
|
|
50
|
+
sa.Column("collapsed", sa.Boolean(), nullable=True, default=False),
|
|
51
|
+
sa.Column("highlight", sa.Boolean(), nullable=True, default=False),
|
|
52
|
+
sa.ForeignKeyConstraint(["job_id"], ["jobs.id"], ondelete="CASCADE"),
|
|
53
|
+
sa.PrimaryKeyConstraint("id"),
|
|
54
|
+
)
|
|
55
|
+
op.create_index(
|
|
56
|
+
"ix_normalized_log_entries_job_id", "normalized_log_entries", ["job_id"]
|
|
57
|
+
)
|
|
58
|
+
op.create_index(
|
|
59
|
+
"ix_normalized_log_entries_job_sequence",
|
|
60
|
+
"normalized_log_entries",
|
|
61
|
+
["job_id", "sequence"],
|
|
62
|
+
)
|
|
63
|
+
op.create_index(
|
|
64
|
+
"ix_normalized_log_entries_entry_type", "normalized_log_entries", ["entry_type"]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
op.drop_index(op.f("ix_agent_sessions_agent_type"), table_name="agent_sessions")
|
|
68
|
+
op.drop_index(op.f("ix_agent_sessions_created_at"), table_name="agent_sessions")
|
|
69
|
+
op.alter_column(
|
|
70
|
+
"boards",
|
|
71
|
+
"created_at",
|
|
72
|
+
existing_type=sa.DATETIME(),
|
|
73
|
+
nullable=False,
|
|
74
|
+
existing_server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
75
|
+
)
|
|
76
|
+
op.alter_column(
|
|
77
|
+
"boards",
|
|
78
|
+
"updated_at",
|
|
79
|
+
existing_type=sa.DATETIME(),
|
|
80
|
+
nullable=False,
|
|
81
|
+
existing_server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
82
|
+
)
|
|
83
|
+
op.create_foreign_key(
|
|
84
|
+
None, "jobs", "revisions", ["source_revision_id"], ["id"], ondelete="SET NULL"
|
|
85
|
+
)
|
|
86
|
+
op.drop_column("jobs", "agent_session_id")
|
|
87
|
+
op.drop_column("jobs", "agent_type")
|
|
88
|
+
op.drop_index(
|
|
89
|
+
op.f("ix_review_comments_revision_resolved"), table_name="review_comments"
|
|
90
|
+
)
|
|
91
|
+
op.drop_index(
|
|
92
|
+
op.f("ix_review_summaries_revision_id"), table_name="review_summaries"
|
|
93
|
+
)
|
|
94
|
+
op.create_index(
|
|
95
|
+
op.f("ix_review_summaries_revision_id"),
|
|
96
|
+
"review_summaries",
|
|
97
|
+
["revision_id"],
|
|
98
|
+
unique=True,
|
|
99
|
+
)
|
|
100
|
+
op.drop_column("tickets", "total_cost_usd")
|
|
101
|
+
op.drop_index(op.f("ix_workspaces_ticket_id"), table_name="workspaces")
|
|
102
|
+
op.create_index(
|
|
103
|
+
op.f("ix_workspaces_ticket_id"), "workspaces", ["ticket_id"], unique=True
|
|
104
|
+
)
|
|
105
|
+
# ### end Alembic commands ###
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def downgrade() -> None:
|
|
109
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
110
|
+
# Drop normalized_log_entries table
|
|
111
|
+
op.drop_index(
|
|
112
|
+
"ix_normalized_log_entries_entry_type", table_name="normalized_log_entries"
|
|
113
|
+
)
|
|
114
|
+
op.drop_index(
|
|
115
|
+
"ix_normalized_log_entries_job_sequence", table_name="normalized_log_entries"
|
|
116
|
+
)
|
|
117
|
+
op.drop_index(
|
|
118
|
+
"ix_normalized_log_entries_job_id", table_name="normalized_log_entries"
|
|
119
|
+
)
|
|
120
|
+
op.drop_table("normalized_log_entries")
|
|
121
|
+
|
|
122
|
+
op.drop_index(op.f("ix_workspaces_ticket_id"), table_name="workspaces")
|
|
123
|
+
op.create_index(
|
|
124
|
+
op.f("ix_workspaces_ticket_id"), "workspaces", ["ticket_id"], unique=False
|
|
125
|
+
)
|
|
126
|
+
op.add_column("tickets", sa.Column("total_cost_usd", sa.FLOAT(), nullable=True))
|
|
127
|
+
op.drop_index(
|
|
128
|
+
op.f("ix_review_summaries_revision_id"), table_name="review_summaries"
|
|
129
|
+
)
|
|
130
|
+
op.create_index(
|
|
131
|
+
op.f("ix_review_summaries_revision_id"),
|
|
132
|
+
"review_summaries",
|
|
133
|
+
["revision_id"],
|
|
134
|
+
unique=False,
|
|
135
|
+
)
|
|
136
|
+
op.create_index(
|
|
137
|
+
op.f("ix_review_comments_revision_resolved"),
|
|
138
|
+
"review_comments",
|
|
139
|
+
["revision_id", "resolved"],
|
|
140
|
+
unique=False,
|
|
141
|
+
)
|
|
142
|
+
op.add_column("jobs", sa.Column("agent_type", sa.VARCHAR(length=50), nullable=True))
|
|
143
|
+
op.add_column(
|
|
144
|
+
"jobs", sa.Column("agent_session_id", sa.VARCHAR(length=36), nullable=True)
|
|
145
|
+
)
|
|
146
|
+
op.drop_constraint(None, "jobs", type_="foreignkey")
|
|
147
|
+
op.alter_column(
|
|
148
|
+
"boards",
|
|
149
|
+
"updated_at",
|
|
150
|
+
existing_type=sa.DATETIME(),
|
|
151
|
+
nullable=True,
|
|
152
|
+
existing_server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
153
|
+
)
|
|
154
|
+
op.alter_column(
|
|
155
|
+
"boards",
|
|
156
|
+
"created_at",
|
|
157
|
+
existing_type=sa.DATETIME(),
|
|
158
|
+
nullable=True,
|
|
159
|
+
existing_server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
|
160
|
+
)
|
|
161
|
+
op.create_index(
|
|
162
|
+
op.f("ix_agent_sessions_created_at"),
|
|
163
|
+
"agent_sessions",
|
|
164
|
+
["created_at"],
|
|
165
|
+
unique=False,
|
|
166
|
+
)
|
|
167
|
+
op.create_index(
|
|
168
|
+
op.f("ix_agent_sessions_agent_type"),
|
|
169
|
+
"agent_sessions",
|
|
170
|
+
["agent_type"],
|
|
171
|
+
unique=False,
|
|
172
|
+
)
|
|
173
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""merge_migration_heads
|
|
2
|
+
|
|
3
|
+
Revision ID: 8f3e2bd8ea3b
|
|
4
|
+
Revises: 03220f0b93ae, perf_indexes_001
|
|
5
|
+
Create Date: 2026-01-29 12:39:52.200213
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision: str = "8f3e2bd8ea3b"
|
|
13
|
+
down_revision: str | None = ("03220f0b93ae", "perf_indexes_001")
|
|
14
|
+
branch_labels: str | Sequence[str] | None = None
|
|
15
|
+
depends_on: str | Sequence[str] | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade() -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def downgrade() -> None:
|
|
23
|
+
pass
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Add config column to boards table
|
|
2
|
+
|
|
3
|
+
Revision ID: 9d17f0698d3b
|
|
4
|
+
Revises: add_repos_001
|
|
5
|
+
Create Date: 2026-02-03 17:11:54.526459
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "9d17f0698d3b"
|
|
17
|
+
down_revision: str | None = "add_repos_001"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
# Add config JSON column to boards table
|
|
24
|
+
# This allows board-level configuration overrides for draft.yaml settings
|
|
25
|
+
op.add_column("boards", sa.Column("config", sa.JSON(), nullable=True))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def downgrade() -> None:
|
|
29
|
+
# Remove config column
|
|
30
|
+
op.drop_column("boards", "config")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Add agent_conversation_history table for UDAR agent memory
|
|
2
|
+
|
|
3
|
+
Revision ID: add_agent_conversation_history
|
|
4
|
+
Revises: b10fb0b62240
|
|
5
|
+
Create Date: 2026-02-09 19:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "add_agent_conversation_history"
|
|
17
|
+
down_revision: str | None = "b10fb0b62240"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
"""Create agent_conversation_history table for UDAR agent memory.
|
|
24
|
+
|
|
25
|
+
This table stores compressed conversation checkpoints (summaries, not full messages)
|
|
26
|
+
to support UDAR agent state persistence across multiple invocations.
|
|
27
|
+
"""
|
|
28
|
+
op.create_table(
|
|
29
|
+
"agent_conversation_history",
|
|
30
|
+
sa.Column("id", sa.String(length=36), nullable=False),
|
|
31
|
+
sa.Column("goal_id", sa.String(length=36), nullable=False),
|
|
32
|
+
sa.Column("checkpoint_id", sa.String(length=100), nullable=False),
|
|
33
|
+
sa.Column(
|
|
34
|
+
"messages_json", sa.Text(), nullable=True
|
|
35
|
+
), # Empty by default (lean storage)
|
|
36
|
+
sa.Column("metadata_json", sa.Text(), nullable=False), # Compressed summary
|
|
37
|
+
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
38
|
+
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
|
39
|
+
sa.PrimaryKeyConstraint("id"),
|
|
40
|
+
sa.ForeignKeyConstraint(
|
|
41
|
+
["goal_id"],
|
|
42
|
+
["goals.id"],
|
|
43
|
+
name="fk_agent_conversation_history_goal_id",
|
|
44
|
+
ondelete="CASCADE", # Delete history when goal deleted
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Index for efficient lookup by goal_id
|
|
49
|
+
op.create_index(
|
|
50
|
+
"ix_agent_conversation_history_goal_id",
|
|
51
|
+
"agent_conversation_history",
|
|
52
|
+
["goal_id"],
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Index for cleanup queries (find old checkpoints)
|
|
56
|
+
op.create_index(
|
|
57
|
+
"ix_agent_conversation_history_created_at",
|
|
58
|
+
"agent_conversation_history",
|
|
59
|
+
["created_at"],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def downgrade() -> None:
|
|
64
|
+
"""Drop agent_conversation_history table."""
|
|
65
|
+
op.drop_index(
|
|
66
|
+
"ix_agent_conversation_history_created_at",
|
|
67
|
+
table_name="agent_conversation_history",
|
|
68
|
+
)
|
|
69
|
+
op.drop_index(
|
|
70
|
+
"ix_agent_conversation_history_goal_id", table_name="agent_conversation_history"
|
|
71
|
+
)
|
|
72
|
+
op.drop_table("agent_conversation_history")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""add_job_variant
|
|
2
|
+
|
|
3
|
+
Revision ID: add_job_variant_001
|
|
4
|
+
Revises: 3348e5cf54c1
|
|
5
|
+
Create Date: 2026-01-29
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
|
|
13
|
+
from alembic import op
|
|
14
|
+
|
|
15
|
+
# revision identifiers, used by Alembic.
|
|
16
|
+
revision: str = "add_job_variant_001"
|
|
17
|
+
down_revision: str | None = "3348e5cf54c1"
|
|
18
|
+
branch_labels: str | Sequence[str] | None = None
|
|
19
|
+
depends_on: str | Sequence[str] | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def upgrade() -> None:
|
|
23
|
+
# Add variant column to jobs table
|
|
24
|
+
op.add_column(
|
|
25
|
+
"jobs",
|
|
26
|
+
sa.Column(
|
|
27
|
+
"variant", sa.String(length=20), nullable=True, server_default="default"
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def downgrade() -> None:
|
|
33
|
+
# Remove variant column from jobs table
|
|
34
|
+
op.drop_column("jobs", "variant")
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Add performance indexes for hot paths.
|
|
2
|
+
|
|
3
|
+
Revision ID: perf_indexes_001
|
|
4
|
+
Revises: 8ef5054dc280
|
|
5
|
+
Create Date: 2026-01-28
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from alembic import op
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision = "perf_indexes_001"
|
|
13
|
+
down_revision = "8ef5054dc280"
|
|
14
|
+
branch_labels = None
|
|
15
|
+
depends_on = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade():
|
|
19
|
+
"""Add performance indexes for frequently queried paths."""
|
|
20
|
+
|
|
21
|
+
# Hot path: Get jobs by status and created_at (for queue status, watchdog)
|
|
22
|
+
op.create_index(
|
|
23
|
+
"idx_jobs_status_created",
|
|
24
|
+
"jobs",
|
|
25
|
+
["status", "created_at"],
|
|
26
|
+
unique=False,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Hot path: Get tickets by state and priority (for board view)
|
|
30
|
+
op.create_index(
|
|
31
|
+
"idx_tickets_state_priority",
|
|
32
|
+
"tickets",
|
|
33
|
+
["state", "priority"],
|
|
34
|
+
unique=False,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Hot path: Get open revisions for a ticket
|
|
38
|
+
op.create_index(
|
|
39
|
+
"idx_revisions_ticket_status",
|
|
40
|
+
"revisions",
|
|
41
|
+
["ticket_id", "status"],
|
|
42
|
+
unique=False,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Hot path: Find evidence by job_id and kind (for diffs)
|
|
46
|
+
op.create_index(
|
|
47
|
+
"idx_evidence_job_kind",
|
|
48
|
+
"evidence",
|
|
49
|
+
["job_id", "kind"],
|
|
50
|
+
unique=False,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Hot path: Job watchdog queries (find stale jobs by heartbeat)
|
|
54
|
+
op.create_index(
|
|
55
|
+
"idx_jobs_status_heartbeat",
|
|
56
|
+
"jobs",
|
|
57
|
+
["status", "last_heartbeat_at"],
|
|
58
|
+
unique=False,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Hot path: Get jobs for a ticket (ordered by creation)
|
|
62
|
+
op.create_index(
|
|
63
|
+
"idx_jobs_ticket_created",
|
|
64
|
+
"jobs",
|
|
65
|
+
["ticket_id", "created_at"],
|
|
66
|
+
unique=False,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Hot path: Get events for a ticket (audit log)
|
|
70
|
+
op.create_index(
|
|
71
|
+
"idx_ticket_events_ticket_created",
|
|
72
|
+
"ticket_events",
|
|
73
|
+
["ticket_id", "created_at"],
|
|
74
|
+
unique=False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Hot path: Rate limiting queries (count recent jobs by ticket/kind/time)
|
|
78
|
+
op.create_index(
|
|
79
|
+
"idx_jobs_ticket_kind_created",
|
|
80
|
+
"jobs",
|
|
81
|
+
["ticket_id", "kind", "created_at"],
|
|
82
|
+
unique=False,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def downgrade():
|
|
87
|
+
"""Remove performance indexes."""
|
|
88
|
+
op.drop_index("idx_jobs_ticket_kind_created", table_name="jobs")
|
|
89
|
+
op.drop_index("idx_ticket_events_ticket_created", table_name="ticket_events")
|
|
90
|
+
op.drop_index("idx_jobs_ticket_created", table_name="jobs")
|
|
91
|
+
op.drop_index("idx_jobs_status_heartbeat", table_name="jobs")
|
|
92
|
+
op.drop_index("idx_evidence_job_kind", table_name="evidence")
|
|
93
|
+
op.drop_index("idx_revisions_ticket_status", table_name="revisions")
|
|
94
|
+
op.drop_index("idx_tickets_state_priority", table_name="tickets")
|
|
95
|
+
op.drop_index("idx_jobs_status_created", table_name="jobs")
|