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,184 @@
|
|
|
1
|
+
"""Tests for webhook notification service."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
import json
|
|
6
|
+
from unittest.mock import AsyncMock, patch
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
from app.services.webhook_service import _build_payload, _sign_payload, fire_webhooks
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_build_payload():
|
|
14
|
+
payload = _build_payload(
|
|
15
|
+
event="ticket.transitioned",
|
|
16
|
+
ticket_id="t1",
|
|
17
|
+
ticket_title="Fix bug",
|
|
18
|
+
board_id="b1",
|
|
19
|
+
from_state="proposed",
|
|
20
|
+
to_state="approved",
|
|
21
|
+
actor_type="human",
|
|
22
|
+
actor_id="user1",
|
|
23
|
+
reason="Looks good",
|
|
24
|
+
)
|
|
25
|
+
assert payload["event"] == "ticket.transitioned"
|
|
26
|
+
assert payload["ticket"]["id"] == "t1"
|
|
27
|
+
assert payload["ticket"]["from_state"] == "proposed"
|
|
28
|
+
assert payload["ticket"]["to_state"] == "approved"
|
|
29
|
+
assert payload["actor"]["type"] == "human"
|
|
30
|
+
assert payload["reason"] == "Looks good"
|
|
31
|
+
assert "timestamp" in payload
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_sign_payload():
|
|
35
|
+
data = b'{"test": true}'
|
|
36
|
+
secret = "mysecret"
|
|
37
|
+
sig = _sign_payload(data, secret)
|
|
38
|
+
expected = hmac.new(secret.encode(), data, hashlib.sha256).hexdigest()
|
|
39
|
+
assert sig == expected
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.asyncio
|
|
43
|
+
async def test_fire_webhooks_empty():
|
|
44
|
+
"""No webhooks = no-op."""
|
|
45
|
+
await fire_webhooks(
|
|
46
|
+
[],
|
|
47
|
+
ticket_id="t1",
|
|
48
|
+
ticket_title="Test",
|
|
49
|
+
board_id="b1",
|
|
50
|
+
from_state="proposed",
|
|
51
|
+
to_state="approved",
|
|
52
|
+
actor_type="human",
|
|
53
|
+
actor_id=None,
|
|
54
|
+
reason=None,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.mark.asyncio
|
|
59
|
+
async def test_fire_webhooks_posts_to_url():
|
|
60
|
+
"""Webhook sends POST with correct payload."""
|
|
61
|
+
mock_response = AsyncMock()
|
|
62
|
+
mock_response.status_code = 200
|
|
63
|
+
|
|
64
|
+
with patch("app.services.webhook_service.httpx.AsyncClient") as mock_client_cls:
|
|
65
|
+
mock_client = AsyncMock()
|
|
66
|
+
mock_client.post.return_value = mock_response
|
|
67
|
+
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
|
68
|
+
mock_client.__aexit__ = AsyncMock(return_value=False)
|
|
69
|
+
mock_client_cls.return_value = mock_client
|
|
70
|
+
|
|
71
|
+
webhooks = [{"id": "w1", "url": "https://example.com/hook", "events": ["*"]}]
|
|
72
|
+
await fire_webhooks(
|
|
73
|
+
webhooks,
|
|
74
|
+
ticket_id="t1",
|
|
75
|
+
ticket_title="Fix bug",
|
|
76
|
+
board_id="b1",
|
|
77
|
+
from_state="proposed",
|
|
78
|
+
to_state="approved",
|
|
79
|
+
actor_type="human",
|
|
80
|
+
actor_id="user1",
|
|
81
|
+
reason=None,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
mock_client.post.assert_called_once()
|
|
85
|
+
call_args = mock_client.post.call_args
|
|
86
|
+
assert call_args[1]["headers"]["Content-Type"] == "application/json"
|
|
87
|
+
payload = json.loads(call_args[1]["content"])
|
|
88
|
+
assert payload["event"] == "ticket.transitioned"
|
|
89
|
+
assert payload["ticket"]["id"] == "t1"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.mark.asyncio
|
|
93
|
+
async def test_fire_webhooks_with_secret():
|
|
94
|
+
"""Webhook includes HMAC signature when secret is set."""
|
|
95
|
+
mock_response = AsyncMock()
|
|
96
|
+
mock_response.status_code = 200
|
|
97
|
+
|
|
98
|
+
with patch("app.services.webhook_service.httpx.AsyncClient") as mock_client_cls:
|
|
99
|
+
mock_client = AsyncMock()
|
|
100
|
+
mock_client.post.return_value = mock_response
|
|
101
|
+
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
|
102
|
+
mock_client.__aexit__ = AsyncMock(return_value=False)
|
|
103
|
+
mock_client_cls.return_value = mock_client
|
|
104
|
+
|
|
105
|
+
webhooks = [
|
|
106
|
+
{
|
|
107
|
+
"id": "w1",
|
|
108
|
+
"url": "https://example.com/hook",
|
|
109
|
+
"events": ["*"],
|
|
110
|
+
"secret": "s3cret",
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
await fire_webhooks(
|
|
114
|
+
webhooks,
|
|
115
|
+
ticket_id="t1",
|
|
116
|
+
ticket_title="Test",
|
|
117
|
+
board_id="b1",
|
|
118
|
+
from_state="proposed",
|
|
119
|
+
to_state="approved",
|
|
120
|
+
actor_type="agent",
|
|
121
|
+
actor_id=None,
|
|
122
|
+
reason=None,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
call_args = mock_client.post.call_args
|
|
126
|
+
headers = call_args[1]["headers"]
|
|
127
|
+
assert "X-Webhook-Signature" in headers
|
|
128
|
+
assert headers["X-Webhook-Signature"].startswith("sha256=")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pytest.mark.asyncio
|
|
132
|
+
async def test_fire_webhooks_event_filter():
|
|
133
|
+
"""Webhook with non-matching event filter is skipped."""
|
|
134
|
+
with patch("app.services.webhook_service.httpx.AsyncClient") as mock_client_cls:
|
|
135
|
+
mock_client = AsyncMock()
|
|
136
|
+
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
|
137
|
+
mock_client.__aexit__ = AsyncMock(return_value=False)
|
|
138
|
+
mock_client_cls.return_value = mock_client
|
|
139
|
+
|
|
140
|
+
webhooks = [
|
|
141
|
+
{
|
|
142
|
+
"id": "w1",
|
|
143
|
+
"url": "https://example.com/hook",
|
|
144
|
+
"events": ["ticket.deleted"],
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
await fire_webhooks(
|
|
148
|
+
webhooks,
|
|
149
|
+
ticket_id="t1",
|
|
150
|
+
ticket_title="Test",
|
|
151
|
+
board_id="b1",
|
|
152
|
+
from_state="proposed",
|
|
153
|
+
to_state="approved",
|
|
154
|
+
actor_type="human",
|
|
155
|
+
actor_id=None,
|
|
156
|
+
reason=None,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
mock_client.post.assert_not_called()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@pytest.mark.asyncio
|
|
163
|
+
async def test_fire_webhooks_error_does_not_raise():
|
|
164
|
+
"""Webhook delivery failure is swallowed (best-effort)."""
|
|
165
|
+
with patch("app.services.webhook_service.httpx.AsyncClient") as mock_client_cls:
|
|
166
|
+
mock_client = AsyncMock()
|
|
167
|
+
mock_client.post.side_effect = Exception("Connection refused")
|
|
168
|
+
mock_client.__aenter__ = AsyncMock(return_value=mock_client)
|
|
169
|
+
mock_client.__aexit__ = AsyncMock(return_value=False)
|
|
170
|
+
mock_client_cls.return_value = mock_client
|
|
171
|
+
|
|
172
|
+
webhooks = [{"id": "w1", "url": "https://example.com/hook", "events": ["*"]}]
|
|
173
|
+
# Should not raise
|
|
174
|
+
await fire_webhooks(
|
|
175
|
+
webhooks,
|
|
176
|
+
ticket_id="t1",
|
|
177
|
+
ticket_title="Test",
|
|
178
|
+
board_id="b1",
|
|
179
|
+
from_state="proposed",
|
|
180
|
+
to_state="approved",
|
|
181
|
+
actor_type="human",
|
|
182
|
+
actor_id=None,
|
|
183
|
+
reason=None,
|
|
184
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tickets": [
|
|
3
|
+
{
|
|
4
|
+
"title": "Add comprehensive API endpoint test coverage",
|
|
5
|
+
"description": "Create integration tests for all API endpoints across routers (boards, tickets, goals, jobs, revisions, etc.). Currently only 6 test files exist covering limited functionality. Tests should cover:\n- CRUD operations for all resources\n- State transitions and validation\n- Error cases and edge cases\n- Board-scoped authorization checks\n- Bulk operations (accept, priority updates)\n\nAcceptance criteria:\n- Test coverage >80% for all routers\n- All endpoints have at least happy path and error case tests\n- Tests use pytest fixtures from conftest.py\n- Tests verify database state changes correctly",
|
|
6
|
+
"priority_bucket": "P1",
|
|
7
|
+
"priority_rationale": "High priority - tests are essential for maintaining code quality and preventing regressions as the codebase grows. Current test coverage is minimal.",
|
|
8
|
+
"verification": [
|
|
9
|
+
"pytest tests/ --cov=app --cov-report=term-missing",
|
|
10
|
+
"pytest tests/ -v --tb=short"
|
|
11
|
+
],
|
|
12
|
+
"notes": "Focus on testing routers/ directory first. Use async test patterns with AsyncSession fixtures. Consider using httpx.AsyncClient for API endpoint testing."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"title": "Add comprehensive API documentation and README",
|
|
16
|
+
"description": "Create detailed documentation for the Smart Kanban backend API including:\n- README.md with setup instructions, architecture overview, and development guide\n- API endpoint documentation (OpenAPI/Swagger is auto-generated but needs enhancement)\n- Database schema documentation\n- Environment variable reference\n- Deployment guide\n- Contributing guidelines\n\nAcceptance criteria:\n- README.md exists with clear setup instructions\n- All environment variables documented in ENV_SETUP.md or README\n- API endpoint examples provided\n- Architecture diagram or description included\n- Database migration guide documented",
|
|
17
|
+
"priority_bucket": "P2",
|
|
18
|
+
"priority_rationale": "Medium priority - documentation improves developer onboarding and maintainability, but doesn't block functionality.",
|
|
19
|
+
"verification": [
|
|
20
|
+
"test -f README.md && grep -q 'setup\\|installation' README.md",
|
|
21
|
+
"grep -r 'DATABASE_URL\\|FRONTEND_URL' .env.example README.md ENV_SETUP.md"
|
|
22
|
+
],
|
|
23
|
+
"notes": "Reference existing ENV_SETUP.md. Include examples from CURL_EXAMPLES.md. Document the board-scoped architecture and permission model."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"title": "Add database migration validation and rollback tests",
|
|
27
|
+
"description": "Implement validation for Alembic migrations to prevent data loss and ensure migrations are reversible:\n- Add migration tests that verify up/down migrations work correctly\n- Validate that migrations don't drop columns without data migration\n- Test migration rollback scenarios\n- Add pre-migration checks for critical schema changes\n- Document migration best practices\n\nAcceptance criteria:\n- Test suite validates all migrations can be applied and rolled back\n- Migrations include data migration steps where needed\n- Pre-migration validation catches dangerous operations\n- Migration guide documents rollback procedures",
|
|
28
|
+
"priority_bucket": "P1",
|
|
29
|
+
"priority_rationale": "High priority - database migrations can cause data loss if not properly tested. Current 12 migration files need validation.",
|
|
30
|
+
"verification": [
|
|
31
|
+
"alembic upgrade head && alembic downgrade -1 && alembic upgrade head",
|
|
32
|
+
"pytest tests/test_migrations.py -v"
|
|
33
|
+
],
|
|
34
|
+
"notes": "Create tests/test_migrations.py. Use Alembic's testing utilities. Focus on migrations that modify or drop columns (especially in ticket, job, goal tables)."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"title": "Implement structured logging and request tracing",
|
|
38
|
+
"description": "Enhance logging throughout the application with structured logging and request tracing:\n- Replace print statements and basic logger calls with structured logging\n- Add request ID tracking for tracing requests through the system\n- Implement correlation IDs for Celery tasks\n- Add performance logging for slow operations\n- Create log aggregation configuration (JSON format for production)\n\nAcceptance criteria:\n- All services use structured logging with context\n- Request IDs propagate through FastAPI → services → Celery tasks\n- Slow operations (>1s) are logged with timing\n- Logs include board_id, ticket_id, goal_id for traceability\n- Production logging uses JSON format",
|
|
39
|
+
"priority_bucket": "P2",
|
|
40
|
+
"priority_rationale": "Medium priority - improves debugging and observability but doesn't block core functionality. Current logging is basic.",
|
|
41
|
+
"verification": [
|
|
42
|
+
"grep -r 'print(' app/ | wc -l",
|
|
43
|
+
"python -c 'import logging; logging.basicConfig(); logging.info({\"test\": \"structured\"})'"
|
|
44
|
+
],
|
|
45
|
+
"notes": "Use structlog or python-json-logger. Add middleware for request ID generation. Update worker.py to include correlation IDs in Celery tasks."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"title": "Add health check endpoints and monitoring metrics",
|
|
49
|
+
"description": "Enhance the existing /health endpoint and add monitoring capabilities:\n- Expand /health to check database connectivity, Redis availability, Celery worker status\n- Add /metrics endpoint for Prometheus-style metrics\n- Track key metrics: job queue depth, ticket state distribution, error rates\n- Add readiness and liveness probes for Kubernetes deployment\n- Implement health check for board repo_root accessibility\n\nAcceptance criteria:\n- /health endpoint returns detailed status of all dependencies\n- /metrics endpoint exposes Prometheus-compatible metrics\n- Health checks fail fast if critical dependencies unavailable\n- Metrics track: active jobs, tickets by state, API request rates, error rates",
|
|
50
|
+
"priority_bucket": "P2",
|
|
51
|
+
"priority_rationale": "Medium priority - monitoring improves production reliability and debugging, but basic health check already exists.",
|
|
52
|
+
"verification": [
|
|
53
|
+
"curl http://localhost:8000/health | jq '.status'",
|
|
54
|
+
"curl http://localhost:8000/metrics | grep -q 'ticket_state_total'"
|
|
55
|
+
],
|
|
56
|
+
"notes": "Use prometheus-fastapi-instrumentator or similar. Check database, Redis, and Celery broker connectivity. Validate board repo_root paths are accessible."
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"tickets": [
|
|
3
|
+
{
|
|
4
|
+
"title": "Create User model and database migration",
|
|
5
|
+
"description": "Create the User SQLAlchemy model with fields: id (UUID), email (unique, indexed), username (unique, indexed), hashed_password, is_active (default True), is_superuser (default False), created_at, updated_at. Create Alembic migration file following the pattern in alembic/versions/012_add_boards_table.py. The User model should extend app.models.base.Base and follow existing model patterns (e.g., app/models/ticket.py). Add proper indexes on email and username for performance.\n\nAcceptance criteria:\n- User model exists in app/models/user.py\n- Migration file created in alembic/versions/013_add_users_table.py\n- Migration can be applied successfully (alembic upgrade head)\n- Model includes all required fields with proper types and constraints\n- Email and username have unique constraints and indexes",
|
|
6
|
+
"priority_bucket": "P0",
|
|
7
|
+
"priority_rationale": "Foundation for all authentication features - without users, nothing else works",
|
|
8
|
+
"verification": [
|
|
9
|
+
"alembic upgrade head",
|
|
10
|
+
"python -c 'from app.models.user import User; print(\"User model imported successfully\")'"
|
|
11
|
+
],
|
|
12
|
+
"notes": "Follow existing model patterns - use String(36) for UUIDs, DateTime with server_default=func.now() for timestamps. Use batch_alter_table for SQLite compatibility if needed."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"title": "Implement JWT authentication system with login endpoint",
|
|
16
|
+
"description": "Implement JWT-based authentication: (1) Add dependencies: python-jose[cryptography] and passlib[bcrypt] to requirements.txt, (2) Create app/auth/security.py with functions: hash_password(), verify_password(), create_access_token(), decode_access_token(), (3) Create app/auth/dependencies.py with get_current_user() dependency that validates JWT tokens from Authorization header, (4) Create POST /auth/login endpoint in app/routers/auth.py that accepts email/username and password, verifies credentials, and returns JWT access token, (5) Add proper error handling for invalid credentials (401), (6) Configure JWT_SECRET_KEY and JWT_ALGORITHM in environment/config.\n\nAcceptance criteria:\n- POST /auth/login returns 200 with access_token on valid credentials\n- POST /auth/login returns 401 on invalid credentials\n- JWT tokens contain user_id and expire after configured time (default 24h)\n- get_current_user dependency raises HTTPException 401 on invalid/missing token\n- Passwords are hashed with bcrypt before storage\n- OpenAPI docs show login endpoint with proper request/response schemas",
|
|
17
|
+
"priority_bucket": "P0",
|
|
18
|
+
"priority_rationale": "Security-critical - authentication is required before any protected endpoints can work",
|
|
19
|
+
"verification": [
|
|
20
|
+
"curl -X POST http://localhost:8000/auth/login -H 'Content-Type: application/json' -d '{\"email\":\"test@example.com\",\"password\":\"test123\"}' | jq .access_token",
|
|
21
|
+
"python -c 'from app.auth.security import hash_password, verify_password; h=hash_password(\"test\"); assert verify_password(\"test\", h)'"
|
|
22
|
+
],
|
|
23
|
+
"notes": "Use HS256 algorithm for JWT. Store JWT_SECRET_KEY in environment variables. Follow FastAPI dependency injection patterns like get_db(). Reference app/routers/tickets.py for router patterns."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"title": "Create user CRUD REST endpoints",
|
|
27
|
+
"description": "Create REST API endpoints for user management: (1) POST /users (create user) - requires email, username, password, returns user without password, (2) GET /users (list users) - returns paginated list, (3) GET /users/{user_id} (get user by ID), (4) PUT /users/{user_id} (update user - email, username, is_active), (5) DELETE /users/{user_id} (soft delete by setting is_active=False). Create app/schemas/user.py with UserCreate, UserUpdate, UserResponse schemas. Create app/services/user_service.py following patterns from app/services/ticket_service.py. Add proper validation, error handling (404 for not found, 409 for duplicate email/username), and ensure passwords are never returned in responses.\n\nAcceptance criteria:\n- All CRUD endpoints exist and are registered in app/main.py\n- POST /users creates user with hashed password\n- GET /users returns list without password fields\n- PUT /users/{user_id} updates user fields\n- DELETE /users/{user_id} sets is_active=False\n- All endpoints return proper HTTP status codes (201, 200, 404, 409)\n- Validation errors return 422 with detail messages\n- OpenAPI documentation is complete for all endpoints",
|
|
28
|
+
"priority_bucket": "P1",
|
|
29
|
+
"priority_rationale": "Core feature requirement - user management API is the main deliverable",
|
|
30
|
+
"verification": [
|
|
31
|
+
"curl -X POST http://localhost:8000/users -H 'Content-Type: application/json' -d '{\"email\":\"user@example.com\",\"username\":\"testuser\",\"password\":\"secure123\"}' | jq",
|
|
32
|
+
"curl http://localhost:8000/users | jq '.total'",
|
|
33
|
+
"curl http://localhost:8000/docs | grep -q 'users'"
|
|
34
|
+
],
|
|
35
|
+
"notes": "Follow existing router patterns from app/routers/tickets.py. Use Pydantic schemas for validation. Ensure UserResponse excludes password field using model_config. Handle duplicate email/username with 409 Conflict status."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"title": "Implement role-based access control (RBAC)",
|
|
39
|
+
"description": "Add role-based access control: (1) Create app/models/enums.py addition or new Role enum with values: ADMIN, USER, VIEWER, (2) Add role column to User model (default USER), create migration, (3) Create app/auth/permissions.py with decorators/dependencies: require_role(), require_admin(), (4) Protect user CRUD endpoints: POST/PUT/DELETE require ADMIN role, GET /users/{user_id} allows same user or ADMIN, GET /users requires ADMIN, (5) Update get_current_user dependency to include role in token payload, (6) Add role to UserResponse schema, (7) Create POST /users/{user_id}/change-role endpoint (admin only) to update user roles.\n\nAcceptance criteria:\n- Role enum exists with ADMIN, USER, VIEWER values\n- User model has role field with default USER\n- require_role() dependency restricts endpoints by role\n- Regular users can only view/edit their own profile\n- Admin users can manage all users\n- JWT tokens include role claim\n- POST /users/{user_id}/change-role endpoint exists and requires ADMIN\n- Unauthorized access returns 403 Forbidden",
|
|
40
|
+
"priority_bucket": "P1",
|
|
41
|
+
"priority_rationale": "Important security feature for multi-user systems, but can be implemented after basic auth",
|
|
42
|
+
"verification": [
|
|
43
|
+
"curl -X GET http://localhost:8000/users -H 'Authorization: Bearer <user_token>' | grep -q '403'",
|
|
44
|
+
"curl -X GET http://localhost:8000/users -H 'Authorization: Bearer <admin_token>' | jq '.total'",
|
|
45
|
+
"python -c 'from app.auth.permissions import require_role; print(\"Permissions module imported\")'"
|
|
46
|
+
],
|
|
47
|
+
"notes": "Use FastAPI dependencies for role checking. Follow middleware patterns but prefer dependency injection. Update JWT token creation to include role. Reference existing authorization patterns in app/routers/tickets.py (board_id checks) for inspiration."
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
File without changes
|
package/app/draft.yaml
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# Draft Configuration
|
|
2
|
+
# This file configures execution and verification pipelines for Draft.
|
|
3
|
+
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# PROJECT CONFIGURATION
|
|
6
|
+
# =============================================================================
|
|
7
|
+
|
|
8
|
+
project:
|
|
9
|
+
# Path to the repository root (relative to this config file or absolute)
|
|
10
|
+
repo_root: "."
|
|
11
|
+
|
|
12
|
+
# =============================================================================
|
|
13
|
+
# EXECUTION CONFIGURATION
|
|
14
|
+
# =============================================================================
|
|
15
|
+
# Settings for the execute job that implements ticket changes using AI code tools.
|
|
16
|
+
#
|
|
17
|
+
# Executor Types:
|
|
18
|
+
# - Claude CLI (headless): Runs automatically, can implement changes without user
|
|
19
|
+
# - Cursor CLI (interactive): Opens editor, transitions to needs_human for manual work
|
|
20
|
+
#
|
|
21
|
+
# State Transitions:
|
|
22
|
+
# - Headless success with diff → verifying
|
|
23
|
+
# - Headless success with NO diff → blocked (reason: no changes)
|
|
24
|
+
# - Headless failure → blocked
|
|
25
|
+
# - Interactive (Cursor) → needs_human immediately
|
|
26
|
+
# - YOLO refused (empty allowlist) → needs_human with explanation
|
|
27
|
+
|
|
28
|
+
execute_config:
|
|
29
|
+
# Timeout for executor CLI in seconds (default: 600 = 10 minutes)
|
|
30
|
+
timeout: 600
|
|
31
|
+
|
|
32
|
+
# Preferred executor CLI
|
|
33
|
+
# Options:
|
|
34
|
+
# - "claude": Claude Code CLI (recommended for automation, headless)
|
|
35
|
+
# - "cursor": Cursor CLI (interactive, opens editor for human)
|
|
36
|
+
preferred_executor: "claude"
|
|
37
|
+
|
|
38
|
+
# Maximum concurrent execute jobs (default: 1 = sequential)
|
|
39
|
+
# Set higher to execute independent tickets in parallel.
|
|
40
|
+
# Dependent tickets (blocked_by) are always executed sequentially.
|
|
41
|
+
max_parallel_jobs: 1
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# YOLO MODE - DANGEROUS: Skip permission prompts
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# When enabled AND allowlist is non-empty, Claude CLI runs with
|
|
47
|
+
# --dangerously-skip-permissions. This allows the AI to run any command
|
|
48
|
+
# without asking for approval.
|
|
49
|
+
#
|
|
50
|
+
# ⚠️ SECURITY WARNING ⚠️
|
|
51
|
+
# Only enable this if you understand the risks:
|
|
52
|
+
# - AI can run ANY shell command without confirmation
|
|
53
|
+
# - Prompt injection attacks could execute malicious code
|
|
54
|
+
# - Network access, file deletion, etc. are all unrestricted
|
|
55
|
+
#
|
|
56
|
+
# Safety Policy (enforced by worker):
|
|
57
|
+
# - If yolo_mode=true BUT yolo_allowlist is empty → REFUSES to run YOLO
|
|
58
|
+
# - Transitions to needs_human with explanation
|
|
59
|
+
# - You MUST explicitly list trusted paths in yolo_allowlist
|
|
60
|
+
# - Only runs inside isolated worktrees (path validation enforced)
|
|
61
|
+
# - Never runs on main/master/develop branches
|
|
62
|
+
#
|
|
63
|
+
# Default: false (permissioned mode - safer)
|
|
64
|
+
yolo_mode: true
|
|
65
|
+
|
|
66
|
+
# REQUIRED when yolo_mode=true: list of trusted repo paths
|
|
67
|
+
# If yolo_mode is true but this is empty, execution REFUSES for safety.
|
|
68
|
+
# This prevents "I turned on YOLO and forgot" accidents.
|
|
69
|
+
# Example: ["/Users/me/trusted-repo", "/home/ci/project"]
|
|
70
|
+
yolo_allowlist: []
|
|
71
|
+
|
|
72
|
+
# =============================================================================
|
|
73
|
+
# EXECUTOR PROFILES
|
|
74
|
+
# =============================================================================
|
|
75
|
+
# Named profiles with per-executor overrides. Use these to configure different
|
|
76
|
+
# execution strategies (e.g., fast vs thorough, different models, timeouts).
|
|
77
|
+
#
|
|
78
|
+
# Select a profile when running a ticket:
|
|
79
|
+
# POST /tickets/{id}/run?executor_profile=fast
|
|
80
|
+
#
|
|
81
|
+
# Example:
|
|
82
|
+
# executor_profiles:
|
|
83
|
+
# fast:
|
|
84
|
+
# executor_type: claude
|
|
85
|
+
# timeout: 300
|
|
86
|
+
# extra_flags: ["--model", "claude-haiku-4-5-20251001"]
|
|
87
|
+
# thorough:
|
|
88
|
+
# executor_type: claude
|
|
89
|
+
# timeout: 1200
|
|
90
|
+
# extra_flags: ["--model", "claude-opus-4-6"]
|
|
91
|
+
# codex-auto:
|
|
92
|
+
# executor_type: codex
|
|
93
|
+
# timeout: 600
|
|
94
|
+
# extra_flags: ["--full-auto"]
|
|
95
|
+
|
|
96
|
+
executor_profiles: {}
|
|
97
|
+
|
|
98
|
+
# =============================================================================
|
|
99
|
+
# VERIFICATION CONFIGURATION
|
|
100
|
+
# =============================================================================
|
|
101
|
+
# Settings for the verify job that validates ticket implementations.
|
|
102
|
+
#
|
|
103
|
+
# State Transitions:
|
|
104
|
+
# - All commands pass → needs_human (awaiting user review/approval)
|
|
105
|
+
# - Any command fails → blocked
|
|
106
|
+
# - User approves revision → done
|
|
107
|
+
|
|
108
|
+
verify_config:
|
|
109
|
+
# Commands to run during ticket verification
|
|
110
|
+
# Each command is executed in the ticket's isolated worktree directory.
|
|
111
|
+
# Commands run sequentially; verification stops on first failure.
|
|
112
|
+
commands:
|
|
113
|
+
- "python -m pytest -q test_calculator.py"
|
|
114
|
+
|
|
115
|
+
# Note: After verification passes, tickets always go to 'needs_human' for review.
|
|
116
|
+
# Only when the user explicitly approves the revision does it move to 'done'.
|
|
117
|
+
# This ensures human oversight of all code changes before completion.
|
|
118
|
+
|
|
119
|
+
# Target state when verification fails (currently only "blocked" supported)
|
|
120
|
+
on_failure: "blocked"
|
|
121
|
+
|
|
122
|
+
# =============================================================================
|
|
123
|
+
# CLEANUP CONFIGURATION
|
|
124
|
+
# =============================================================================
|
|
125
|
+
# Controls automatic cleanup of worktrees and evidence files to prevent disk rot.
|
|
126
|
+
# Use POST /maintenance/cleanup to trigger cleanup manually.
|
|
127
|
+
|
|
128
|
+
cleanup_config:
|
|
129
|
+
# Delete worktree after successful merge
|
|
130
|
+
auto_cleanup_on_merge: true
|
|
131
|
+
# Delete worktrees older than this many days
|
|
132
|
+
worktree_ttl_days: 14
|
|
133
|
+
# Delete evidence files older than this many days
|
|
134
|
+
evidence_ttl_days: 30
|
|
135
|
+
# Maximum number of active worktrees (for future use)
|
|
136
|
+
max_worktrees: 50
|
|
137
|
+
|
|
138
|
+
# =============================================================================
|
|
139
|
+
# MERGE CONFIGURATION
|
|
140
|
+
# =============================================================================
|
|
141
|
+
# Controls how worktree branches are merged back into the default branch.
|
|
142
|
+
# Use POST /tickets/{id}/merge to trigger merge after approval.
|
|
143
|
+
|
|
144
|
+
merge_config:
|
|
145
|
+
# Default merge strategy: "merge" or "rebase"
|
|
146
|
+
default_strategy: "merge"
|
|
147
|
+
# Pull --ff-only before merge to ensure we're up to date
|
|
148
|
+
pull_before_merge: true
|
|
149
|
+
# Delete the feature branch after successful merge
|
|
150
|
+
delete_branch_after_merge: true
|
|
151
|
+
|
|
152
|
+
# =============================================================================
|
|
153
|
+
# AUTONOMY CONFIGURATION
|
|
154
|
+
# =============================================================================
|
|
155
|
+
# Safety rails for full autonomy mode. When a goal has autonomy_enabled=true,
|
|
156
|
+
# these settings control what the system is allowed to auto-approve.
|
|
157
|
+
#
|
|
158
|
+
# Individual gates (auto_approve_tickets, auto_approve_revisions, auto_merge,
|
|
159
|
+
# auto_approve_followups) are set per-goal via the API.
|
|
160
|
+
# These global settings define safety limits that apply to ALL autonomous goals.
|
|
161
|
+
|
|
162
|
+
autonomy_config:
|
|
163
|
+
# Maximum diff lines allowed for auto-approval of revisions.
|
|
164
|
+
# Diffs larger than this require human review.
|
|
165
|
+
max_diff_lines: 500
|
|
166
|
+
|
|
167
|
+
# File patterns that trigger mandatory human review.
|
|
168
|
+
# If any file in the diff matches these patterns, auto-approval is blocked.
|
|
169
|
+
sensitive_file_patterns:
|
|
170
|
+
- "**/.env*"
|
|
171
|
+
- "**/*.pem"
|
|
172
|
+
- "**/*.key"
|
|
173
|
+
- "**/secrets/**"
|
|
174
|
+
- "**/credentials*"
|
|
175
|
+
|
|
176
|
+
# Require all verification commands to pass before auto-approving.
|
|
177
|
+
# When true (default), any verification failure blocks auto-approval.
|
|
178
|
+
require_verification_pass: true
|
|
179
|
+
|
|
180
|
+
# =============================================================================
|
|
181
|
+
# PLANNER CONFIGURATION
|
|
182
|
+
# =============================================================================
|
|
183
|
+
# Settings for the AI planner that automates workflow decisions.
|
|
184
|
+
#
|
|
185
|
+
# The planner runs in "tick" mode - each tick evaluates the board state and
|
|
186
|
+
# takes one or more actions:
|
|
187
|
+
# - Pick next ticket: If PLANNED tickets exist and nothing is EXECUTING
|
|
188
|
+
# - Propose follow-ups: For BLOCKED tickets, generate follow-up ticket proposals
|
|
189
|
+
# - Generate reflections: For DONE tickets, create summary comments
|
|
190
|
+
#
|
|
191
|
+
# LLM is only used for follow-ups and reflections. Ticket selection is deterministic.
|
|
192
|
+
|
|
193
|
+
planner_config:
|
|
194
|
+
# Agent CLI path for ticket generation (cursor-agent or claude)
|
|
195
|
+
# Auto-detected from PATH; set full path to override (e.g., "~/.local/bin/cursor-agent")
|
|
196
|
+
agent_path: "claude"
|
|
197
|
+
|
|
198
|
+
# LLM model for the planner.
|
|
199
|
+
# Default: "cli/claude" — uses the same Claude Code CLI as the executor (no API key needed).
|
|
200
|
+
# To use an API model instead, change in Settings > Executors or set here:
|
|
201
|
+
# Direct Anthropic API: "anthropic/claude-sonnet-4-5-20250929" (requires ANTHROPIC_API_KEY)
|
|
202
|
+
# AWS Bedrock: "bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0" (requires AWS credentials)
|
|
203
|
+
# Bedrock inf. profile: "bedrock/arn:aws:bedrock:us-east-2:...:inference-profile/..."
|
|
204
|
+
# OpenAI: "gpt-4o-mini" or "gpt-4o" (requires OPENAI_API_KEY)
|
|
205
|
+
model: "cli/claude"
|
|
206
|
+
|
|
207
|
+
# Maximum tokens for LLM responses
|
|
208
|
+
max_tokens_reflection: 300
|
|
209
|
+
max_tokens_followup: 500
|
|
210
|
+
|
|
211
|
+
# Timeout for LLM API calls in seconds
|
|
212
|
+
timeout: 30
|
|
213
|
+
|
|
214
|
+
# ---------------------------------------------------------------------------
|
|
215
|
+
# SAFETY CAPS - Prevent runaway follow-up generation
|
|
216
|
+
# ---------------------------------------------------------------------------
|
|
217
|
+
# Follow-ups for blocked tickets can explode your backlog if uncapped.
|
|
218
|
+
# These limits ensure the planner doesn't spam your board.
|
|
219
|
+
|
|
220
|
+
# Maximum follow-up tickets for any single blocked ticket (total, ever)
|
|
221
|
+
max_followups_per_ticket: 2
|
|
222
|
+
|
|
223
|
+
# Maximum follow-up tickets created in one tick
|
|
224
|
+
max_followups_per_tick: 3
|
|
225
|
+
|
|
226
|
+
# Blocker reasons that should NOT trigger follow-ups
|
|
227
|
+
# These are typically prompt/requirements issues, not new tickets
|
|
228
|
+
skip_followup_reasons:
|
|
229
|
+
- "no changes produced"
|
|
230
|
+
- "no changes"
|
|
231
|
+
- "empty diff"
|
|
232
|
+
|
|
233
|
+
# Enable/disable specific planner features
|
|
234
|
+
features:
|
|
235
|
+
# Automatically pick and execute highest priority PLANNED ticket
|
|
236
|
+
# When false, tickets stay in "planned" until you click "Start Autopilot"
|
|
237
|
+
# When true, the periodic planner tick will automatically start executing planned tickets
|
|
238
|
+
auto_execute: false
|
|
239
|
+
# Generate follow-up ticket proposals for BLOCKED tickets
|
|
240
|
+
propose_followups: true
|
|
241
|
+
# Generate reflection summaries for DONE tickets
|
|
242
|
+
generate_reflections: true
|
|
243
|
+
# Validate generated tickets against codebase before creating them
|
|
244
|
+
# When true, the LLM checks if tickets are:
|
|
245
|
+
# - Appropriate for the goal
|
|
246
|
+
# - Not already implemented
|
|
247
|
+
# - Relevant to the codebase
|
|
248
|
+
# Invalid tickets are filtered out and logged
|
|
249
|
+
validate_tickets: false # Disabled for testing - agent output will generate all tickets
|
|
250
|
+
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
# UDAR AGENT CONFIGURATION (Experimental)
|
|
253
|
+
# ---------------------------------------------------------------------------
|
|
254
|
+
# UDAR (Understand-Decide-Act-Validate-Review) is a lean agent architecture
|
|
255
|
+
# for adaptive ticket generation. When enabled, it replaces the legacy
|
|
256
|
+
# single-shot ticket generator with a structured workflow that:
|
|
257
|
+
# - Gathers codebase context deterministically (0 LLM calls)
|
|
258
|
+
# - Generates tickets in batched LLM call (1 LLM call)
|
|
259
|
+
# - Validates proposals deterministically (0 LLM calls)
|
|
260
|
+
# - Can optionally re-plan after ticket completion (incremental)
|
|
261
|
+
#
|
|
262
|
+
# COST: 1-2 LLM calls per goal (vs legacy 1 call), but with better reasoning
|
|
263
|
+
udar:
|
|
264
|
+
# Enable UDAR agent for ticket generation (default: false for Phase 2 testing)
|
|
265
|
+
enabled: false
|
|
266
|
+
|
|
267
|
+
# Enable incremental replanning after ticket completion (default: false)
|
|
268
|
+
# When true, agent analyzes completed tickets and generates follow-ups
|
|
269
|
+
# Cost: ~1 LLM call per 5 completed tickets (batched)
|
|
270
|
+
enable_incremental_replanning: false
|
|
271
|
+
|
|
272
|
+
# Max self-correction iterations if validation fails (default: 1 to save quota)
|
|
273
|
+
max_self_correction_iterations: 1
|
|
274
|
+
|
|
275
|
+
# Enable optional LLM validation in Validate phase (default: false)
|
|
276
|
+
# When false, only deterministic validation is used (exact duplicate check, etc.)
|
|
277
|
+
# When true, adds LLM-based validation (+1 LLM call)
|
|
278
|
+
enable_llm_validation: false
|
|
279
|
+
|
|
280
|
+
# Incremental replanning settings (Phase 3)
|
|
281
|
+
# Batch size: Wait for this many completed tickets before analyzing (default: 5)
|
|
282
|
+
# Higher = fewer LLM calls but less responsive follow-ups
|
|
283
|
+
replan_batch_size: 5
|
|
284
|
+
|
|
285
|
+
# Significance threshold: Only call LLM if files changed exceeds this (default: 10)
|
|
286
|
+
# Changes below this threshold are considered "minor" and skip LLM analysis
|
|
287
|
+
replan_significance_threshold: 10
|
|
288
|
+
|
|
289
|
+
# Max frequency: Minimum minutes between replanning batches (default: 30)
|
|
290
|
+
# Prevents too-frequent LLM calls even if batch size reached
|
|
291
|
+
replan_max_frequency_minutes: 30
|
|
292
|
+
|
|
293
|
+
# ---------------------------------------------------------------------------
|
|
294
|
+
# Production Hardening (Phase 5)
|
|
295
|
+
# ---------------------------------------------------------------------------
|
|
296
|
+
# Error handling, timeouts, and fallback behavior for production deployments
|
|
297
|
+
|
|
298
|
+
# Fallback to legacy ticket generation if UDAR fails (default: true)
|
|
299
|
+
# When true, errors in UDAR automatically fall back to legacy mode
|
|
300
|
+
# When false, UDAR errors propagate to the caller
|
|
301
|
+
fallback_to_legacy: true
|
|
302
|
+
|
|
303
|
+
# Timeout for UDAR agent execution in seconds (default: 120 = 2 minutes)
|
|
304
|
+
# Prevents runaway LLM calls from blocking indefinitely
|
|
305
|
+
timeout_seconds: 120
|
|
306
|
+
|
|
307
|
+
# Enable cost tracking in AgentSession table (default: true)
|
|
308
|
+
# Tracks input/output tokens and estimated USD cost per goal
|
|
309
|
+
enable_cost_tracking: true
|
|
310
|
+
|
|
311
|
+
# Max retries on transient errors (default: 0 = no retry)
|
|
312
|
+
# For network errors, rate limits, etc. NOT for validation failures.
|
|
313
|
+
max_retries_on_error: 0
|