gobby 0.2.5__py3-none-any.whl

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.
Files changed (383) hide show
  1. gobby/__init__.py +3 -0
  2. gobby/adapters/__init__.py +30 -0
  3. gobby/adapters/base.py +93 -0
  4. gobby/adapters/claude_code.py +276 -0
  5. gobby/adapters/codex.py +1292 -0
  6. gobby/adapters/gemini.py +343 -0
  7. gobby/agents/__init__.py +37 -0
  8. gobby/agents/codex_session.py +120 -0
  9. gobby/agents/constants.py +112 -0
  10. gobby/agents/context.py +362 -0
  11. gobby/agents/definitions.py +133 -0
  12. gobby/agents/gemini_session.py +111 -0
  13. gobby/agents/registry.py +618 -0
  14. gobby/agents/runner.py +968 -0
  15. gobby/agents/session.py +259 -0
  16. gobby/agents/spawn.py +916 -0
  17. gobby/agents/spawners/__init__.py +77 -0
  18. gobby/agents/spawners/base.py +142 -0
  19. gobby/agents/spawners/cross_platform.py +266 -0
  20. gobby/agents/spawners/embedded.py +225 -0
  21. gobby/agents/spawners/headless.py +226 -0
  22. gobby/agents/spawners/linux.py +125 -0
  23. gobby/agents/spawners/macos.py +277 -0
  24. gobby/agents/spawners/windows.py +308 -0
  25. gobby/agents/tty_config.py +319 -0
  26. gobby/autonomous/__init__.py +32 -0
  27. gobby/autonomous/progress_tracker.py +447 -0
  28. gobby/autonomous/stop_registry.py +269 -0
  29. gobby/autonomous/stuck_detector.py +383 -0
  30. gobby/cli/__init__.py +67 -0
  31. gobby/cli/__main__.py +8 -0
  32. gobby/cli/agents.py +529 -0
  33. gobby/cli/artifacts.py +266 -0
  34. gobby/cli/daemon.py +329 -0
  35. gobby/cli/extensions.py +526 -0
  36. gobby/cli/github.py +263 -0
  37. gobby/cli/init.py +53 -0
  38. gobby/cli/install.py +614 -0
  39. gobby/cli/installers/__init__.py +37 -0
  40. gobby/cli/installers/antigravity.py +65 -0
  41. gobby/cli/installers/claude.py +363 -0
  42. gobby/cli/installers/codex.py +192 -0
  43. gobby/cli/installers/gemini.py +294 -0
  44. gobby/cli/installers/git_hooks.py +377 -0
  45. gobby/cli/installers/shared.py +737 -0
  46. gobby/cli/linear.py +250 -0
  47. gobby/cli/mcp.py +30 -0
  48. gobby/cli/mcp_proxy.py +698 -0
  49. gobby/cli/memory.py +304 -0
  50. gobby/cli/merge.py +384 -0
  51. gobby/cli/projects.py +79 -0
  52. gobby/cli/sessions.py +622 -0
  53. gobby/cli/tasks/__init__.py +30 -0
  54. gobby/cli/tasks/_utils.py +658 -0
  55. gobby/cli/tasks/ai.py +1025 -0
  56. gobby/cli/tasks/commits.py +169 -0
  57. gobby/cli/tasks/crud.py +685 -0
  58. gobby/cli/tasks/deps.py +135 -0
  59. gobby/cli/tasks/labels.py +63 -0
  60. gobby/cli/tasks/main.py +273 -0
  61. gobby/cli/tasks/search.py +178 -0
  62. gobby/cli/tui.py +34 -0
  63. gobby/cli/utils.py +513 -0
  64. gobby/cli/workflows.py +927 -0
  65. gobby/cli/worktrees.py +481 -0
  66. gobby/config/__init__.py +129 -0
  67. gobby/config/app.py +551 -0
  68. gobby/config/extensions.py +167 -0
  69. gobby/config/features.py +472 -0
  70. gobby/config/llm_providers.py +98 -0
  71. gobby/config/logging.py +66 -0
  72. gobby/config/mcp.py +346 -0
  73. gobby/config/persistence.py +247 -0
  74. gobby/config/servers.py +141 -0
  75. gobby/config/sessions.py +250 -0
  76. gobby/config/tasks.py +784 -0
  77. gobby/hooks/__init__.py +104 -0
  78. gobby/hooks/artifact_capture.py +213 -0
  79. gobby/hooks/broadcaster.py +243 -0
  80. gobby/hooks/event_handlers.py +723 -0
  81. gobby/hooks/events.py +218 -0
  82. gobby/hooks/git.py +169 -0
  83. gobby/hooks/health_monitor.py +171 -0
  84. gobby/hooks/hook_manager.py +856 -0
  85. gobby/hooks/hook_types.py +575 -0
  86. gobby/hooks/plugins.py +813 -0
  87. gobby/hooks/session_coordinator.py +396 -0
  88. gobby/hooks/verification_runner.py +268 -0
  89. gobby/hooks/webhooks.py +339 -0
  90. gobby/install/claude/commands/gobby/bug.md +51 -0
  91. gobby/install/claude/commands/gobby/chore.md +51 -0
  92. gobby/install/claude/commands/gobby/epic.md +52 -0
  93. gobby/install/claude/commands/gobby/eval.md +235 -0
  94. gobby/install/claude/commands/gobby/feat.md +49 -0
  95. gobby/install/claude/commands/gobby/nit.md +52 -0
  96. gobby/install/claude/commands/gobby/ref.md +52 -0
  97. gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
  98. gobby/install/claude/hooks/hook_dispatcher.py +364 -0
  99. gobby/install/claude/hooks/validate_settings.py +102 -0
  100. gobby/install/claude/hooks-template.json +118 -0
  101. gobby/install/codex/hooks/hook_dispatcher.py +153 -0
  102. gobby/install/codex/prompts/forget.md +7 -0
  103. gobby/install/codex/prompts/memories.md +7 -0
  104. gobby/install/codex/prompts/recall.md +7 -0
  105. gobby/install/codex/prompts/remember.md +13 -0
  106. gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
  107. gobby/install/gemini/hooks-template.json +138 -0
  108. gobby/install/shared/plugins/code_guardian.py +456 -0
  109. gobby/install/shared/plugins/example_notify.py +331 -0
  110. gobby/integrations/__init__.py +10 -0
  111. gobby/integrations/github.py +145 -0
  112. gobby/integrations/linear.py +145 -0
  113. gobby/llm/__init__.py +40 -0
  114. gobby/llm/base.py +120 -0
  115. gobby/llm/claude.py +578 -0
  116. gobby/llm/claude_executor.py +503 -0
  117. gobby/llm/codex.py +322 -0
  118. gobby/llm/codex_executor.py +513 -0
  119. gobby/llm/executor.py +316 -0
  120. gobby/llm/factory.py +34 -0
  121. gobby/llm/gemini.py +258 -0
  122. gobby/llm/gemini_executor.py +339 -0
  123. gobby/llm/litellm.py +287 -0
  124. gobby/llm/litellm_executor.py +303 -0
  125. gobby/llm/resolver.py +499 -0
  126. gobby/llm/service.py +236 -0
  127. gobby/mcp_proxy/__init__.py +29 -0
  128. gobby/mcp_proxy/actions.py +175 -0
  129. gobby/mcp_proxy/daemon_control.py +198 -0
  130. gobby/mcp_proxy/importer.py +436 -0
  131. gobby/mcp_proxy/lazy.py +325 -0
  132. gobby/mcp_proxy/manager.py +798 -0
  133. gobby/mcp_proxy/metrics.py +609 -0
  134. gobby/mcp_proxy/models.py +139 -0
  135. gobby/mcp_proxy/registries.py +215 -0
  136. gobby/mcp_proxy/schema_hash.py +381 -0
  137. gobby/mcp_proxy/semantic_search.py +706 -0
  138. gobby/mcp_proxy/server.py +549 -0
  139. gobby/mcp_proxy/services/__init__.py +0 -0
  140. gobby/mcp_proxy/services/fallback.py +306 -0
  141. gobby/mcp_proxy/services/recommendation.py +224 -0
  142. gobby/mcp_proxy/services/server_mgmt.py +214 -0
  143. gobby/mcp_proxy/services/system.py +72 -0
  144. gobby/mcp_proxy/services/tool_filter.py +231 -0
  145. gobby/mcp_proxy/services/tool_proxy.py +309 -0
  146. gobby/mcp_proxy/stdio.py +565 -0
  147. gobby/mcp_proxy/tools/__init__.py +27 -0
  148. gobby/mcp_proxy/tools/agents.py +1103 -0
  149. gobby/mcp_proxy/tools/artifacts.py +207 -0
  150. gobby/mcp_proxy/tools/hub.py +335 -0
  151. gobby/mcp_proxy/tools/internal.py +337 -0
  152. gobby/mcp_proxy/tools/memory.py +543 -0
  153. gobby/mcp_proxy/tools/merge.py +422 -0
  154. gobby/mcp_proxy/tools/metrics.py +283 -0
  155. gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
  156. gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
  157. gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
  158. gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
  159. gobby/mcp_proxy/tools/orchestration/review.py +736 -0
  160. gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
  161. gobby/mcp_proxy/tools/session_messages.py +1056 -0
  162. gobby/mcp_proxy/tools/task_dependencies.py +219 -0
  163. gobby/mcp_proxy/tools/task_expansion.py +591 -0
  164. gobby/mcp_proxy/tools/task_github.py +393 -0
  165. gobby/mcp_proxy/tools/task_linear.py +379 -0
  166. gobby/mcp_proxy/tools/task_orchestration.py +77 -0
  167. gobby/mcp_proxy/tools/task_readiness.py +522 -0
  168. gobby/mcp_proxy/tools/task_sync.py +351 -0
  169. gobby/mcp_proxy/tools/task_validation.py +843 -0
  170. gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
  171. gobby/mcp_proxy/tools/tasks/_context.py +112 -0
  172. gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
  173. gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
  174. gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
  175. gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
  176. gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
  177. gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
  178. gobby/mcp_proxy/tools/tasks/_search.py +215 -0
  179. gobby/mcp_proxy/tools/tasks/_session.py +125 -0
  180. gobby/mcp_proxy/tools/workflows.py +973 -0
  181. gobby/mcp_proxy/tools/worktrees.py +1264 -0
  182. gobby/mcp_proxy/transports/__init__.py +0 -0
  183. gobby/mcp_proxy/transports/base.py +95 -0
  184. gobby/mcp_proxy/transports/factory.py +44 -0
  185. gobby/mcp_proxy/transports/http.py +139 -0
  186. gobby/mcp_proxy/transports/stdio.py +213 -0
  187. gobby/mcp_proxy/transports/websocket.py +136 -0
  188. gobby/memory/backends/__init__.py +116 -0
  189. gobby/memory/backends/mem0.py +408 -0
  190. gobby/memory/backends/memu.py +485 -0
  191. gobby/memory/backends/null.py +111 -0
  192. gobby/memory/backends/openmemory.py +537 -0
  193. gobby/memory/backends/sqlite.py +304 -0
  194. gobby/memory/context.py +87 -0
  195. gobby/memory/manager.py +1001 -0
  196. gobby/memory/protocol.py +451 -0
  197. gobby/memory/search/__init__.py +66 -0
  198. gobby/memory/search/text.py +127 -0
  199. gobby/memory/viz.py +258 -0
  200. gobby/prompts/__init__.py +13 -0
  201. gobby/prompts/defaults/expansion/system.md +119 -0
  202. gobby/prompts/defaults/expansion/user.md +48 -0
  203. gobby/prompts/defaults/external_validation/agent.md +72 -0
  204. gobby/prompts/defaults/external_validation/external.md +63 -0
  205. gobby/prompts/defaults/external_validation/spawn.md +83 -0
  206. gobby/prompts/defaults/external_validation/system.md +6 -0
  207. gobby/prompts/defaults/features/import_mcp.md +22 -0
  208. gobby/prompts/defaults/features/import_mcp_github.md +17 -0
  209. gobby/prompts/defaults/features/import_mcp_search.md +16 -0
  210. gobby/prompts/defaults/features/recommend_tools.md +32 -0
  211. gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
  212. gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
  213. gobby/prompts/defaults/features/server_description.md +20 -0
  214. gobby/prompts/defaults/features/server_description_system.md +6 -0
  215. gobby/prompts/defaults/features/task_description.md +31 -0
  216. gobby/prompts/defaults/features/task_description_system.md +6 -0
  217. gobby/prompts/defaults/features/tool_summary.md +17 -0
  218. gobby/prompts/defaults/features/tool_summary_system.md +6 -0
  219. gobby/prompts/defaults/research/step.md +58 -0
  220. gobby/prompts/defaults/validation/criteria.md +47 -0
  221. gobby/prompts/defaults/validation/validate.md +38 -0
  222. gobby/prompts/loader.py +346 -0
  223. gobby/prompts/models.py +113 -0
  224. gobby/py.typed +0 -0
  225. gobby/runner.py +488 -0
  226. gobby/search/__init__.py +23 -0
  227. gobby/search/protocol.py +104 -0
  228. gobby/search/tfidf.py +232 -0
  229. gobby/servers/__init__.py +7 -0
  230. gobby/servers/http.py +636 -0
  231. gobby/servers/models.py +31 -0
  232. gobby/servers/routes/__init__.py +23 -0
  233. gobby/servers/routes/admin.py +416 -0
  234. gobby/servers/routes/dependencies.py +118 -0
  235. gobby/servers/routes/mcp/__init__.py +24 -0
  236. gobby/servers/routes/mcp/hooks.py +135 -0
  237. gobby/servers/routes/mcp/plugins.py +121 -0
  238. gobby/servers/routes/mcp/tools.py +1337 -0
  239. gobby/servers/routes/mcp/webhooks.py +159 -0
  240. gobby/servers/routes/sessions.py +582 -0
  241. gobby/servers/websocket.py +766 -0
  242. gobby/sessions/__init__.py +13 -0
  243. gobby/sessions/analyzer.py +322 -0
  244. gobby/sessions/lifecycle.py +240 -0
  245. gobby/sessions/manager.py +563 -0
  246. gobby/sessions/processor.py +225 -0
  247. gobby/sessions/summary.py +532 -0
  248. gobby/sessions/transcripts/__init__.py +41 -0
  249. gobby/sessions/transcripts/base.py +125 -0
  250. gobby/sessions/transcripts/claude.py +386 -0
  251. gobby/sessions/transcripts/codex.py +143 -0
  252. gobby/sessions/transcripts/gemini.py +195 -0
  253. gobby/storage/__init__.py +21 -0
  254. gobby/storage/agents.py +409 -0
  255. gobby/storage/artifact_classifier.py +341 -0
  256. gobby/storage/artifacts.py +285 -0
  257. gobby/storage/compaction.py +67 -0
  258. gobby/storage/database.py +357 -0
  259. gobby/storage/inter_session_messages.py +194 -0
  260. gobby/storage/mcp.py +680 -0
  261. gobby/storage/memories.py +562 -0
  262. gobby/storage/merge_resolutions.py +550 -0
  263. gobby/storage/migrations.py +860 -0
  264. gobby/storage/migrations_legacy.py +1359 -0
  265. gobby/storage/projects.py +166 -0
  266. gobby/storage/session_messages.py +251 -0
  267. gobby/storage/session_tasks.py +97 -0
  268. gobby/storage/sessions.py +817 -0
  269. gobby/storage/task_dependencies.py +223 -0
  270. gobby/storage/tasks/__init__.py +42 -0
  271. gobby/storage/tasks/_aggregates.py +180 -0
  272. gobby/storage/tasks/_crud.py +449 -0
  273. gobby/storage/tasks/_id.py +104 -0
  274. gobby/storage/tasks/_lifecycle.py +311 -0
  275. gobby/storage/tasks/_manager.py +889 -0
  276. gobby/storage/tasks/_models.py +300 -0
  277. gobby/storage/tasks/_ordering.py +119 -0
  278. gobby/storage/tasks/_path_cache.py +110 -0
  279. gobby/storage/tasks/_queries.py +343 -0
  280. gobby/storage/tasks/_search.py +143 -0
  281. gobby/storage/workflow_audit.py +393 -0
  282. gobby/storage/worktrees.py +547 -0
  283. gobby/sync/__init__.py +29 -0
  284. gobby/sync/github.py +333 -0
  285. gobby/sync/linear.py +304 -0
  286. gobby/sync/memories.py +284 -0
  287. gobby/sync/tasks.py +641 -0
  288. gobby/tasks/__init__.py +8 -0
  289. gobby/tasks/build_verification.py +193 -0
  290. gobby/tasks/commits.py +633 -0
  291. gobby/tasks/context.py +747 -0
  292. gobby/tasks/criteria.py +342 -0
  293. gobby/tasks/enhanced_validator.py +226 -0
  294. gobby/tasks/escalation.py +263 -0
  295. gobby/tasks/expansion.py +626 -0
  296. gobby/tasks/external_validator.py +764 -0
  297. gobby/tasks/issue_extraction.py +171 -0
  298. gobby/tasks/prompts/expand.py +327 -0
  299. gobby/tasks/research.py +421 -0
  300. gobby/tasks/tdd.py +352 -0
  301. gobby/tasks/tree_builder.py +263 -0
  302. gobby/tasks/validation.py +712 -0
  303. gobby/tasks/validation_history.py +357 -0
  304. gobby/tasks/validation_models.py +89 -0
  305. gobby/tools/__init__.py +0 -0
  306. gobby/tools/summarizer.py +170 -0
  307. gobby/tui/__init__.py +5 -0
  308. gobby/tui/api_client.py +281 -0
  309. gobby/tui/app.py +327 -0
  310. gobby/tui/screens/__init__.py +25 -0
  311. gobby/tui/screens/agents.py +333 -0
  312. gobby/tui/screens/chat.py +450 -0
  313. gobby/tui/screens/dashboard.py +377 -0
  314. gobby/tui/screens/memory.py +305 -0
  315. gobby/tui/screens/metrics.py +231 -0
  316. gobby/tui/screens/orchestrator.py +904 -0
  317. gobby/tui/screens/sessions.py +412 -0
  318. gobby/tui/screens/tasks.py +442 -0
  319. gobby/tui/screens/workflows.py +289 -0
  320. gobby/tui/screens/worktrees.py +174 -0
  321. gobby/tui/widgets/__init__.py +21 -0
  322. gobby/tui/widgets/chat.py +210 -0
  323. gobby/tui/widgets/conductor.py +104 -0
  324. gobby/tui/widgets/menu.py +132 -0
  325. gobby/tui/widgets/message_panel.py +160 -0
  326. gobby/tui/widgets/review_gate.py +224 -0
  327. gobby/tui/widgets/task_tree.py +99 -0
  328. gobby/tui/widgets/token_budget.py +166 -0
  329. gobby/tui/ws_client.py +258 -0
  330. gobby/utils/__init__.py +3 -0
  331. gobby/utils/daemon_client.py +235 -0
  332. gobby/utils/git.py +222 -0
  333. gobby/utils/id.py +38 -0
  334. gobby/utils/json_helpers.py +161 -0
  335. gobby/utils/logging.py +376 -0
  336. gobby/utils/machine_id.py +135 -0
  337. gobby/utils/metrics.py +589 -0
  338. gobby/utils/project_context.py +182 -0
  339. gobby/utils/project_init.py +263 -0
  340. gobby/utils/status.py +256 -0
  341. gobby/utils/validation.py +80 -0
  342. gobby/utils/version.py +23 -0
  343. gobby/workflows/__init__.py +4 -0
  344. gobby/workflows/actions.py +1310 -0
  345. gobby/workflows/approval_flow.py +138 -0
  346. gobby/workflows/artifact_actions.py +103 -0
  347. gobby/workflows/audit_helpers.py +110 -0
  348. gobby/workflows/autonomous_actions.py +286 -0
  349. gobby/workflows/context_actions.py +394 -0
  350. gobby/workflows/definitions.py +130 -0
  351. gobby/workflows/detection_helpers.py +208 -0
  352. gobby/workflows/engine.py +485 -0
  353. gobby/workflows/evaluator.py +669 -0
  354. gobby/workflows/git_utils.py +96 -0
  355. gobby/workflows/hooks.py +169 -0
  356. gobby/workflows/lifecycle_evaluator.py +613 -0
  357. gobby/workflows/llm_actions.py +70 -0
  358. gobby/workflows/loader.py +333 -0
  359. gobby/workflows/mcp_actions.py +60 -0
  360. gobby/workflows/memory_actions.py +272 -0
  361. gobby/workflows/premature_stop.py +164 -0
  362. gobby/workflows/session_actions.py +139 -0
  363. gobby/workflows/state_actions.py +123 -0
  364. gobby/workflows/state_manager.py +104 -0
  365. gobby/workflows/stop_signal_actions.py +163 -0
  366. gobby/workflows/summary_actions.py +344 -0
  367. gobby/workflows/task_actions.py +249 -0
  368. gobby/workflows/task_enforcement_actions.py +901 -0
  369. gobby/workflows/templates.py +52 -0
  370. gobby/workflows/todo_actions.py +84 -0
  371. gobby/workflows/webhook.py +223 -0
  372. gobby/workflows/webhook_executor.py +399 -0
  373. gobby/worktrees/__init__.py +5 -0
  374. gobby/worktrees/git.py +690 -0
  375. gobby/worktrees/merge/__init__.py +20 -0
  376. gobby/worktrees/merge/conflict_parser.py +177 -0
  377. gobby/worktrees/merge/resolver.py +485 -0
  378. gobby-0.2.5.dist-info/METADATA +351 -0
  379. gobby-0.2.5.dist-info/RECORD +383 -0
  380. gobby-0.2.5.dist-info/WHEEL +5 -0
  381. gobby-0.2.5.dist-info/entry_points.txt +2 -0
  382. gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
  383. gobby-0.2.5.dist-info/top_level.txt +1 -0
gobby/config/tasks.py ADDED
@@ -0,0 +1,784 @@
1
+ """
2
+ Task management configuration module.
3
+
4
+ Contains task-related Pydantic config models:
5
+ - CompactHandoffConfig: Compact handoff context configuration
6
+ - PatternCriteriaConfig: Pattern-specific validation criteria templates
7
+ - TaskExpansionConfig: Task breakdown/expansion settings
8
+ - TaskValidationConfig: Task completion validation settings
9
+ - GobbyTasksConfig: Combined gobby-tasks MCP server config
10
+ - WorkflowConfig: Workflow engine configuration
11
+ - WorkflowVariablesConfig: Default values for session workflow variables
12
+
13
+ Extracted from app.py using Strangler Fig pattern for code decomposition.
14
+ """
15
+
16
+ from typing import Any, Literal
17
+
18
+ from pydantic import BaseModel, Field, field_validator
19
+
20
+ __all__ = [
21
+ "CompactHandoffConfig",
22
+ "FileExtractionConfig",
23
+ "PatternCriteriaConfig",
24
+ "TaskEnrichmentConfig",
25
+ "TaskExpansionConfig",
26
+ "TaskValidationConfig",
27
+ "GobbyTasksConfig",
28
+ "WorkflowConfig",
29
+ "WorkflowVariablesConfig",
30
+ "merge_workflow_variables",
31
+ ]
32
+
33
+
34
+ class CompactHandoffConfig(BaseModel):
35
+ """Compact handoff context configuration for /compact command."""
36
+
37
+ enabled: bool = Field(
38
+ default=True,
39
+ description="Enable compact handoff context extraction and injection",
40
+ )
41
+ # DEPRECATED: prompt field is no longer used.
42
+ # Template is now defined in session-handoff.yaml workflow file.
43
+ # Kept for backwards compatibility but will be removed in a future version.
44
+ prompt: str | None = Field(
45
+ default=None,
46
+ description="DEPRECATED: Template moved to session-handoff.yaml workflow. "
47
+ "This field is ignored.",
48
+ )
49
+
50
+
51
+ class PatternCriteriaConfig(BaseModel):
52
+ """Configuration for pattern-specific validation criteria templates.
53
+
54
+ Defines validation criteria templates for common development patterns like
55
+ strangler-fig, TDD, and refactoring. Templates can use placeholders that
56
+ get replaced with actual values from project verification config.
57
+
58
+ Placeholders:
59
+ - {unit_tests}: Unit test command from project verification
60
+ - {type_check}: Type check command from project verification
61
+ - {lint}: Lint command from project verification
62
+ - {original_module}, {new_module}, {function}, {original_file}: For strangler-fig pattern
63
+ """
64
+
65
+ patterns: dict[str, list[str]] = Field(
66
+ default_factory=lambda: {
67
+ "strangler-fig": [
68
+ "Original import still works: `from {original_module} import {function}`",
69
+ "New import works: `from {new_module} import {function}`",
70
+ "Delegation exists: `grep -c 'from .{new_module} import' {original_file}` >= 1",
71
+ "No circular imports: `python -c 'from {original_module} import *'`",
72
+ ],
73
+ "tdd": [
74
+ "Tests written before implementation (verify git log order)",
75
+ "Tests initially fail (red phase)",
76
+ "Implementation makes tests pass (green phase)",
77
+ ],
78
+ "refactoring": [
79
+ "All existing tests pass: `{unit_tests}`",
80
+ "No new type errors: `{type_check}`",
81
+ "No lint violations: `{lint}`",
82
+ ],
83
+ },
84
+ description="Pattern name to list of validation criteria templates. "
85
+ "Templates can use placeholders like {unit_tests}, {type_check}, {lint}.",
86
+ )
87
+ detection_keywords: dict[str, list[str]] = Field(
88
+ default_factory=lambda: {
89
+ "strangler-fig": [
90
+ "strangler fig",
91
+ "strangler-fig",
92
+ "strangler pattern",
93
+ "delegation pattern",
94
+ ],
95
+ "tdd": ["tdd", "test-driven", "test driven", "red-green", "red green"],
96
+ "refactoring": ["refactor", "refactoring", "restructure", "reorganize"],
97
+ },
98
+ description="Pattern name to list of keywords that trigger pattern detection in task descriptions.",
99
+ )
100
+
101
+
102
+ class TaskEnrichmentConfig(BaseModel):
103
+ """Configuration for task enrichment (adding context, categorization, validation criteria)."""
104
+
105
+ enabled: bool = Field(
106
+ default=True,
107
+ description="Enable task enrichment",
108
+ )
109
+ provider: str = Field(
110
+ default="claude",
111
+ description="LLM provider to use for enrichment",
112
+ )
113
+ model: str = Field(
114
+ default="claude-3-5-haiku-latest",
115
+ description="Model to use for enrichment (lightweight model for speed)",
116
+ )
117
+ enable_code_research: bool = Field(
118
+ default=True,
119
+ description="Enable codebase research during enrichment",
120
+ )
121
+ enable_web_research: bool = Field(
122
+ default=False,
123
+ description="Enable web research during enrichment",
124
+ )
125
+ enable_mcp_tools: bool = Field(
126
+ default=False,
127
+ description="Enable MCP tool calls during enrichment",
128
+ )
129
+ generate_validation: bool = Field(
130
+ default=True,
131
+ description="Generate validation criteria during enrichment",
132
+ )
133
+
134
+
135
+ class TaskExpansionConfig(BaseModel):
136
+ """Configuration for task expansion (breaking down broad tasks/epics)."""
137
+
138
+ enabled: bool = Field(
139
+ default=True,
140
+ description="Enable automated task expansion",
141
+ )
142
+ provider: str = Field(
143
+ default="claude",
144
+ description="LLM provider to use for expansion",
145
+ )
146
+ model: str = Field(
147
+ default="claude-opus-4-5",
148
+ description="Model to use for expansion",
149
+ )
150
+ prompt: str | None = Field(
151
+ default=None,
152
+ description="DEPRECATED: Use prompt_path instead. Custom prompt template for task expansion",
153
+ )
154
+ prompt_path: str | None = Field(
155
+ default=None,
156
+ description="Path to custom user prompt template (e.g., 'expansion/user')",
157
+ )
158
+ system_prompt_path: str | None = Field(
159
+ default=None,
160
+ description="Path to custom system prompt template (e.g., 'expansion/system')",
161
+ )
162
+ codebase_research_enabled: bool = Field(
163
+ default=True,
164
+ description="Enable agentic codebase research for context gathering",
165
+ )
166
+ research_model: str | None = Field(
167
+ default=None,
168
+ description="Model to use for research agent (defaults to expansion model if None)",
169
+ )
170
+ research_max_steps: int = Field(
171
+ default=10,
172
+ description="Maximum number of steps for research agent loop",
173
+ )
174
+ research_system_prompt: str = Field(
175
+ default="You are a senior developer researching a codebase. Use tools to find relevant code.",
176
+ description="System prompt for the research agent",
177
+ )
178
+ system_prompt: str | None = Field(
179
+ default=None,
180
+ description="DEPRECATED: Use system_prompt_path instead. Custom system prompt for task expansion",
181
+ )
182
+ tdd_prompt: str | None = Field(
183
+ default=None,
184
+ description="DEPRECATED: TDD mode is now integrated into the system prompt template via Jinja2 conditionals",
185
+ )
186
+ web_research_enabled: bool = Field(
187
+ default=True,
188
+ description="Enable web research for task expansion using MCP tools",
189
+ )
190
+ tdd_mode: bool = Field(
191
+ default=True,
192
+ description="Enable TDD mode: create test->implement task pairs with appropriate blocking for coding tasks",
193
+ )
194
+ max_subtasks: int = Field(
195
+ default=15,
196
+ description="Maximum number of subtasks to create per expansion",
197
+ )
198
+ default_strategy: Literal["auto", "phased", "sequential", "parallel"] = Field(
199
+ default="auto",
200
+ description="Default expansion strategy: auto (LLM decides), phased, sequential, or parallel",
201
+ )
202
+ timeout: float = Field(
203
+ default=300.0,
204
+ description="Maximum time in seconds for entire task expansion (default: 5 minutes)",
205
+ )
206
+ research_timeout: float = Field(
207
+ default=60.0,
208
+ description="Maximum time in seconds for research phase (default: 60 seconds)",
209
+ )
210
+ pattern_criteria: PatternCriteriaConfig = Field(
211
+ default_factory=PatternCriteriaConfig,
212
+ description="Pattern-specific validation criteria templates",
213
+ )
214
+
215
+
216
+ class TaskValidationConfig(BaseModel):
217
+ """Configuration for task validation (checking completion against criteria)."""
218
+
219
+ enabled: bool = Field(
220
+ default=True,
221
+ description="Enable automated task validation",
222
+ )
223
+ provider: str = Field(
224
+ default="claude",
225
+ description="LLM provider to use for validation",
226
+ )
227
+ model: str = Field(
228
+ default="claude-opus-4-5",
229
+ description="Model to use for validation",
230
+ )
231
+ system_prompt: str = Field(
232
+ default="You are a QA validator. Output ONLY valid JSON. No markdown, no explanation, no code blocks. Just the raw JSON object.",
233
+ description="System prompt for task validation",
234
+ )
235
+ prompt: str | None = Field(
236
+ default=None,
237
+ description="DEPRECATED: Use prompt_path instead. Custom prompt template for task validation",
238
+ )
239
+ prompt_path: str | None = Field(
240
+ default=None,
241
+ description="Path to custom validation prompt template (e.g., 'validation/validate')",
242
+ )
243
+ criteria_prompt_path: str | None = Field(
244
+ default=None,
245
+ description="Path to custom criteria generation prompt template (e.g., 'validation/criteria')",
246
+ )
247
+ external_system_prompt_path: str | None = Field(
248
+ default=None,
249
+ description="Path to external validator system prompt (e.g., 'external_validation/system')",
250
+ )
251
+ external_spawn_prompt_path: str | None = Field(
252
+ default=None,
253
+ description="Path to spawn validation prompt template (e.g., 'external_validation/spawn')",
254
+ )
255
+ external_agent_prompt_path: str | None = Field(
256
+ default=None,
257
+ description="Path to agent validation prompt template (e.g., 'external_validation/agent')",
258
+ )
259
+ external_llm_prompt_path: str | None = Field(
260
+ default=None,
261
+ description="Path to LLM validation prompt template (e.g., 'external_validation/external')",
262
+ )
263
+ criteria_system_prompt: str = Field(
264
+ default="You are a QA engineer writing acceptance criteria. CRITICAL: Only include requirements explicitly stated in the task. Do NOT invent specific values, thresholds, timeouts, or edge cases that aren't mentioned. Vague tasks get vague criteria. Use markdown checkboxes.",
265
+ description="System prompt for generating validation criteria",
266
+ )
267
+ criteria_prompt: str | None = Field(
268
+ default=None,
269
+ description="DEPRECATED: Use criteria_prompt_path instead. Custom prompt template for generating validation criteria",
270
+ )
271
+ # Validation loop control
272
+ max_iterations: int = Field(
273
+ default=10,
274
+ description="Maximum validation attempts before escalation",
275
+ )
276
+ max_consecutive_errors: int = Field(
277
+ default=3,
278
+ description="Max consecutive errors before stopping validation loop",
279
+ )
280
+ recurring_issue_threshold: int = Field(
281
+ default=3,
282
+ description="Number of times same issue can recur before escalation",
283
+ )
284
+ issue_similarity_threshold: float = Field(
285
+ default=0.8,
286
+ description="Similarity threshold (0-1) for detecting recurring issues",
287
+ )
288
+ # Build verification
289
+ run_build_first: bool = Field(
290
+ default=True,
291
+ description="Run build/test command before LLM validation",
292
+ )
293
+ build_command: str | None = Field(
294
+ default=None,
295
+ description="Custom build command (auto-detected if None: npm test, pytest, etc.)",
296
+ )
297
+ # External validator
298
+ use_external_validator: bool = Field(
299
+ default=False,
300
+ description="Use external LLM for validation (different from task agent)",
301
+ )
302
+ external_validator_model: str | None = Field(
303
+ default=None,
304
+ description="Model for external validation (defaults to validation.model)",
305
+ )
306
+ external_validator_mode: Literal["llm", "agent", "spawn"] = Field(
307
+ default="llm",
308
+ description="External validator mode: 'llm' uses direct API calls, "
309
+ "'agent' uses in-process agent with tools, "
310
+ "'spawn' spawns a separate headless agent process via gobby-agents",
311
+ )
312
+ # Escalation settings
313
+ escalation_enabled: bool = Field(
314
+ default=True,
315
+ description="Enable task escalation on repeated validation failures",
316
+ )
317
+ escalation_notify: Literal["webhook", "slack", "none"] = Field(
318
+ default="none",
319
+ description="Notification method when task is escalated",
320
+ )
321
+ escalation_webhook_url: str | None = Field(
322
+ default=None,
323
+ description="Webhook URL for escalation notifications",
324
+ )
325
+ # Auto-generation settings
326
+ auto_generate_on_create: bool = Field(
327
+ default=True,
328
+ description="Auto-generate validation criteria when creating tasks via create_task",
329
+ )
330
+ auto_generate_on_expand: bool = Field(
331
+ default=True,
332
+ description="Auto-generate validation criteria when expanding tasks via expand_task",
333
+ )
334
+
335
+ @field_validator("max_iterations", "max_consecutive_errors", "recurring_issue_threshold")
336
+ @classmethod
337
+ def validate_positive_int(cls, v: int) -> int:
338
+ """Validate value is positive."""
339
+ if v <= 0:
340
+ raise ValueError("Value must be positive")
341
+ return v
342
+
343
+ @field_validator("issue_similarity_threshold")
344
+ @classmethod
345
+ def validate_threshold(cls, v: float) -> float:
346
+ """Validate threshold is between 0 and 1."""
347
+ if not 0 <= v <= 1:
348
+ raise ValueError("issue_similarity_threshold must be between 0 and 1")
349
+ return v
350
+
351
+
352
+ class FileExtractionConfig(BaseModel):
353
+ """Configuration for extracting file paths from task descriptions.
354
+
355
+ Used by extract_mentioned_files() to identify task-relevant files
356
+ for validation context prioritization.
357
+ """
358
+
359
+ # Comprehensive list of file extensions to recognize
360
+ file_extensions: list[str] = Field(
361
+ default_factory=lambda: [
362
+ # Python
363
+ ".py",
364
+ ".pyi",
365
+ ".pyx",
366
+ ".pxd",
367
+ # JavaScript/TypeScript
368
+ ".js",
369
+ ".jsx",
370
+ ".ts",
371
+ ".tsx",
372
+ ".mjs",
373
+ ".cjs",
374
+ # Web
375
+ ".html",
376
+ ".htm",
377
+ ".css",
378
+ ".scss",
379
+ ".sass",
380
+ ".less",
381
+ ".vue",
382
+ ".svelte",
383
+ ".astro",
384
+ # Data/Config
385
+ ".json",
386
+ ".yaml",
387
+ ".yml",
388
+ ".toml",
389
+ ".ini",
390
+ ".cfg",
391
+ ".conf",
392
+ ".env",
393
+ ".xml",
394
+ ".csv",
395
+ ".tsv",
396
+ # Documentation
397
+ ".md",
398
+ ".markdown",
399
+ ".rst",
400
+ ".txt",
401
+ ".adoc",
402
+ # Shell/Scripts
403
+ ".sh",
404
+ ".bash",
405
+ ".zsh",
406
+ ".fish",
407
+ ".ps1",
408
+ ".bat",
409
+ ".cmd",
410
+ # Go
411
+ ".go",
412
+ ".mod",
413
+ ".sum",
414
+ # Rust
415
+ ".rs",
416
+ ".toml",
417
+ # Java/Kotlin/JVM
418
+ ".java",
419
+ ".kt",
420
+ ".kts",
421
+ ".scala",
422
+ ".groovy",
423
+ ".gradle",
424
+ # C/C++
425
+ ".c",
426
+ ".h",
427
+ ".cpp",
428
+ ".hpp",
429
+ ".cc",
430
+ ".hh",
431
+ ".cxx",
432
+ ".hxx",
433
+ # C#/.NET
434
+ ".cs",
435
+ ".csproj",
436
+ ".sln",
437
+ ".fs",
438
+ ".fsx",
439
+ # Swift/Objective-C
440
+ ".swift",
441
+ ".m",
442
+ ".mm",
443
+ # Ruby
444
+ ".rb",
445
+ ".rake",
446
+ ".gemspec",
447
+ # PHP
448
+ ".php",
449
+ ".phtml",
450
+ # Perl
451
+ ".pl",
452
+ ".pm",
453
+ # Lua
454
+ ".lua",
455
+ # R
456
+ ".r",
457
+ ".R",
458
+ ".rmd",
459
+ ".Rmd",
460
+ # Julia
461
+ ".jl",
462
+ # Elixir/Erlang
463
+ ".ex",
464
+ ".exs",
465
+ ".erl",
466
+ ".hrl",
467
+ # Haskell
468
+ ".hs",
469
+ ".lhs",
470
+ # OCaml
471
+ ".ml",
472
+ ".mli",
473
+ # SQL
474
+ ".sql",
475
+ ".psql",
476
+ ".mysql",
477
+ # GraphQL
478
+ ".graphql",
479
+ ".gql",
480
+ # Protobuf/Thrift
481
+ ".proto",
482
+ ".thrift",
483
+ # Docker/Container
484
+ ".dockerfile",
485
+ # Terraform/IaC
486
+ ".tf",
487
+ ".tfvars",
488
+ ".hcl",
489
+ # Kubernetes
490
+ ".k8s",
491
+ # Nix
492
+ ".nix",
493
+ # Prisma
494
+ ".prisma",
495
+ # Other
496
+ ".lock",
497
+ ".log",
498
+ ".diff",
499
+ ".patch",
500
+ ],
501
+ description="File extensions to recognize when extracting paths from task descriptions",
502
+ )
503
+
504
+ # Files without extensions that should be recognized
505
+ known_files: list[str] = Field(
506
+ default_factory=lambda: [
507
+ "Makefile",
508
+ "makefile",
509
+ "GNUmakefile",
510
+ "Dockerfile",
511
+ "dockerfile",
512
+ "Containerfile",
513
+ "Jenkinsfile",
514
+ "Vagrantfile",
515
+ "Rakefile",
516
+ "rakefile",
517
+ "Gemfile",
518
+ "Podfile",
519
+ "Brewfile",
520
+ "Procfile",
521
+ "Taskfile",
522
+ "Justfile",
523
+ "justfile",
524
+ "Earthfile",
525
+ "Tiltfile",
526
+ "BUILD",
527
+ "WORKSPACE",
528
+ "CMakeLists",
529
+ "meson.build",
530
+ "SConstruct",
531
+ "SConscript",
532
+ "CHANGELOG",
533
+ "CHANGES",
534
+ "HISTORY",
535
+ "README",
536
+ "INSTALL",
537
+ "LICENSE",
538
+ "COPYING",
539
+ "AUTHORS",
540
+ "CONTRIBUTORS",
541
+ "MAINTAINERS",
542
+ "CODEOWNERS",
543
+ ".gitignore",
544
+ ".gitattributes",
545
+ ".gitmodules",
546
+ ".dockerignore",
547
+ ".editorconfig",
548
+ ".eslintrc",
549
+ ".prettierrc",
550
+ ".stylelintrc",
551
+ ".babelrc",
552
+ ".nvmrc",
553
+ ".node-version",
554
+ ".python-version",
555
+ ".ruby-version",
556
+ "requirements.txt",
557
+ "constraints.txt",
558
+ "package.json",
559
+ "package-lock.json",
560
+ "yarn.lock",
561
+ "pnpm-lock.yaml",
562
+ "Cargo.toml",
563
+ "Cargo.lock",
564
+ "go.mod",
565
+ "go.sum",
566
+ "composer.json",
567
+ "composer.lock",
568
+ "pyproject.toml",
569
+ "setup.py",
570
+ "setup.cfg",
571
+ "poetry.lock",
572
+ "Pipfile",
573
+ "Pipfile.lock",
574
+ "tsconfig.json",
575
+ "jsconfig.json",
576
+ "webpack.config.js",
577
+ "vite.config.js",
578
+ "rollup.config.js",
579
+ "jest.config.js",
580
+ "vitest.config.js",
581
+ "playwright.config.js",
582
+ ".env.local",
583
+ ".env.development",
584
+ ".env.production",
585
+ ".env.test",
586
+ ],
587
+ description="Known filenames without extensions to recognize",
588
+ )
589
+
590
+ # Common path prefixes that indicate a file path
591
+ path_prefixes: list[str] = Field(
592
+ default_factory=lambda: [
593
+ "src/",
594
+ "lib/",
595
+ "pkg/",
596
+ "packages/",
597
+ "test/",
598
+ "tests/",
599
+ "spec/",
600
+ "specs/",
601
+ "__tests__/",
602
+ "app/",
603
+ "apps/",
604
+ "internal/",
605
+ "cmd/",
606
+ "bin/",
607
+ "scripts/",
608
+ "tools/",
609
+ "utils/",
610
+ "config/",
611
+ "configs/",
612
+ "conf/",
613
+ "settings/",
614
+ "docs/",
615
+ "doc/",
616
+ "documentation/",
617
+ "assets/",
618
+ "static/",
619
+ "public/",
620
+ "resources/",
621
+ "components/",
622
+ "pages/",
623
+ "views/",
624
+ "templates/",
625
+ "models/",
626
+ "controllers/",
627
+ "services/",
628
+ "handlers/",
629
+ "api/",
630
+ "routes/",
631
+ "middleware/",
632
+ "fixtures/",
633
+ "mocks/",
634
+ "stubs/",
635
+ "fakes/",
636
+ "migrations/",
637
+ "seeds/",
638
+ "schemas/",
639
+ ".github/",
640
+ ".circleci/",
641
+ ".gitlab/",
642
+ ".gobby/",
643
+ ".vscode/",
644
+ ".idea/",
645
+ ],
646
+ description="Common path prefixes that indicate a file path",
647
+ )
648
+
649
+
650
+ class GobbyTasksConfig(BaseModel):
651
+ """Configuration for gobby-tasks internal MCP server."""
652
+
653
+ model_config = {"populate_by_name": True}
654
+
655
+ enabled: bool = Field(
656
+ default=True,
657
+ description="Enable gobby-tasks internal MCP server",
658
+ )
659
+ show_result_on_create: bool = Field(
660
+ default=False,
661
+ description="Show full task result on create_task (False = minimal output with just id)",
662
+ )
663
+ file_extraction: FileExtractionConfig = Field(
664
+ default_factory=FileExtractionConfig,
665
+ description="Configuration for extracting file paths from task descriptions",
666
+ )
667
+ enrichment: TaskEnrichmentConfig = Field(
668
+ default_factory=TaskEnrichmentConfig,
669
+ description="Task enrichment configuration",
670
+ )
671
+ expansion: TaskExpansionConfig = Field(
672
+ default_factory=lambda: TaskExpansionConfig(),
673
+ description="Task expansion configuration",
674
+ )
675
+ validation: TaskValidationConfig = Field(
676
+ default_factory=lambda: TaskValidationConfig(),
677
+ description="Task validation configuration",
678
+ )
679
+
680
+
681
+ class WorkflowConfig(BaseModel):
682
+ """Workflow engine configuration."""
683
+
684
+ enabled: bool = Field(
685
+ default=True,
686
+ description="Enable workflow engine",
687
+ )
688
+ timeout: float = Field(
689
+ default=0.0,
690
+ description="Timeout in seconds for workflow execution. 0 = no timeout (default)",
691
+ )
692
+ require_task_before_edit: bool = Field(
693
+ default=False,
694
+ description="Require an active gobby-task (in_progress) before allowing Edit/Write tools",
695
+ )
696
+ protected_tools: list[str] = Field(
697
+ default_factory=lambda: ["Edit", "Write", "Update", "NotebookEdit"],
698
+ description="Tools that require an active task when require_task_before_edit is enabled",
699
+ )
700
+
701
+ @field_validator("timeout")
702
+ @classmethod
703
+ def validate_timeout(cls, v: float) -> float:
704
+ """Validate timeout is non-negative."""
705
+ if v < 0:
706
+ raise ValueError("Timeout must be non-negative (0 = no timeout)")
707
+ return v
708
+
709
+
710
+ class WorkflowVariablesConfig(BaseModel):
711
+ """Default values for session workflow variables.
712
+
713
+ These defaults are used when workflow YAML files don't specify values.
714
+ Variables can be overridden per-session via set_variable MCP tool.
715
+
716
+ The precedence order is:
717
+ 1. Explicit parameter (highest priority)
718
+ 2. Workflow variable (set at runtime)
719
+ 3. Workflow YAML default
720
+ 4. This config (lowest priority)
721
+ """
722
+
723
+ require_task_before_edit: bool = Field(
724
+ default=False,
725
+ description="Require an active task (in_progress) before allowing file edits",
726
+ )
727
+ tdd_mode: bool = Field(
728
+ default=True,
729
+ description="Enable TDD mode for task expansion (test-implementation pairs)",
730
+ )
731
+ session_task: str | list[str] | None = Field(
732
+ default=None,
733
+ description="Task(s) to complete before stopping. "
734
+ "Values: None (no enforcement), task ID, list of IDs, or '*' (all ready tasks)",
735
+ )
736
+
737
+
738
+ def merge_workflow_variables(
739
+ yaml_defaults: dict[str, Any],
740
+ db_overrides: dict[str, Any] | None = None,
741
+ validate: bool = True,
742
+ ) -> dict[str, Any]:
743
+ """Merge workflow YAML defaults with DB session overrides.
744
+
745
+ Implements the merge order: YAML defaults → DB overrides → effective config.
746
+ DB overrides take precedence over YAML defaults.
747
+
748
+ Args:
749
+ yaml_defaults: Variable defaults from workflow YAML definition.
750
+ db_overrides: Session-specific overrides from DB workflow_states.variables.
751
+ Can be None if no session state exists.
752
+ validate: If True, validate merged result through WorkflowVariablesConfig.
753
+ Invalid values will raise ValidationError.
754
+
755
+ Returns:
756
+ Effective config dict with merged variables that actions can access.
757
+
758
+ Raises:
759
+ ValidationError: If validate=True and merged values fail validation.
760
+
761
+ Example:
762
+ >>> yaml_defaults = {"tdd_mode": True, "require_task_before_edit": False}
763
+ >>> db_overrides = {"tdd_mode": False}
764
+ >>> effective = merge_workflow_variables(yaml_defaults, db_overrides)
765
+ >>> effective["tdd_mode"]
766
+ False
767
+ >>> effective["require_task_before_edit"]
768
+ False
769
+ """
770
+ # Start with defaults
771
+ effective = dict(yaml_defaults)
772
+
773
+ # Apply DB overrides (takes precedence)
774
+ if db_overrides:
775
+ effective.update(db_overrides)
776
+
777
+ # Validate through WorkflowVariablesConfig if requested
778
+ if validate:
779
+ # This will raise ValidationError for invalid values
780
+ validated = WorkflowVariablesConfig(**effective)
781
+ # Return as dict for action access
782
+ return validated.model_dump()
783
+
784
+ return effective