devcouncil 0.3.0 → 0.4.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.
Files changed (365) hide show
  1. package/README.md +46 -30
  2. package/package.json +6 -2
  3. package/packages/codeintel-grammars/hatch_build.py +43 -0
  4. package/packages/codeintel-grammars/pyproject.toml +16 -0
  5. package/packages/codeintel-grammars/src/devcouncil_codeintel_grammars/__init__.py +93 -0
  6. package/pyproject.toml +99 -4
  7. package/src/devcouncil/app/config.py +512 -20
  8. package/src/devcouncil/app/events.py +4 -23
  9. package/src/devcouncil/app/orchestrator.py +5 -0
  10. package/src/devcouncil/app/run_context.py +3 -3
  11. package/src/devcouncil/assets/__init__.py +4 -1
  12. package/src/devcouncil/assets/vendor/force-graph.min.js +5 -0
  13. package/src/devcouncil/campaign/__init__.py +71 -0
  14. package/src/devcouncil/campaign/bloom.py +137 -0
  15. package/src/devcouncil/campaign/dashboard.py +123 -0
  16. package/src/devcouncil/campaign/mailbox.py +305 -0
  17. package/src/devcouncil/campaign/notify.py +91 -0
  18. package/src/devcouncil/campaign/orchestrator.py +592 -0
  19. package/src/devcouncil/campaign/prompts/coordinator.md +29 -0
  20. package/src/devcouncil/campaign/prompts/director.md +21 -0
  21. package/src/devcouncil/campaign/prompts/protocol.md +46 -0
  22. package/src/devcouncil/campaign/prompts/reviewer.md +24 -0
  23. package/src/devcouncil/campaign/prompts/worker.md +24 -0
  24. package/src/devcouncil/campaign/roles.py +202 -0
  25. package/src/devcouncil/campaign/watcher.py +153 -0
  26. package/src/devcouncil/cli/commands/agents.py +24 -17
  27. package/src/devcouncil/cli/commands/artifacts.py +36 -27
  28. package/src/devcouncil/cli/commands/ast.py +12 -3
  29. package/src/devcouncil/cli/commands/baseline.py +21 -12
  30. package/src/devcouncil/cli/commands/boot.py +218 -0
  31. package/src/devcouncil/cli/commands/campaign.py +302 -0
  32. package/src/devcouncil/cli/commands/check.py +225 -12
  33. package/src/devcouncil/cli/commands/config.py +221 -74
  34. package/src/devcouncil/cli/commands/cost.py +137 -28
  35. package/src/devcouncil/cli/commands/dashboard.py +12 -4
  36. package/src/devcouncil/cli/commands/debug_cmd.py +249 -0
  37. package/src/devcouncil/cli/commands/design.py +27 -17
  38. package/src/devcouncil/cli/commands/doctor.py +790 -8
  39. package/src/devcouncil/cli/commands/evidence.py +41 -20
  40. package/src/devcouncil/cli/commands/export.py +73 -0
  41. package/src/devcouncil/cli/commands/gaps.py +175 -0
  42. package/src/devcouncil/cli/commands/gated_write.py +76 -0
  43. package/src/devcouncil/cli/commands/go.py +220 -68
  44. package/src/devcouncil/cli/commands/graph_cmd.py +1192 -0
  45. package/src/devcouncil/cli/commands/handoff.py +45 -34
  46. package/src/devcouncil/cli/commands/hook.py +630 -85
  47. package/src/devcouncil/cli/commands/init.py +89 -30
  48. package/src/devcouncil/cli/commands/integrate.py +296 -1385
  49. package/src/devcouncil/cli/commands/lease.py +120 -0
  50. package/src/devcouncil/cli/commands/logs.py +12 -5
  51. package/src/devcouncil/cli/commands/lsp.py +40 -5
  52. package/src/devcouncil/cli/commands/map.py +317 -74
  53. package/src/devcouncil/cli/commands/mcp_server.py +12 -2
  54. package/src/devcouncil/cli/commands/okf.py +44 -6
  55. package/src/devcouncil/cli/commands/plan.py +184 -69
  56. package/src/devcouncil/cli/commands/prompt.py +26 -17
  57. package/src/devcouncil/cli/commands/provenance.py +79 -0
  58. package/src/devcouncil/cli/commands/repair.py +60 -49
  59. package/src/devcouncil/cli/commands/report.py +148 -40
  60. package/src/devcouncil/cli/commands/requirements.py +104 -0
  61. package/src/devcouncil/cli/commands/reset_demo_state.py +13 -4
  62. package/src/devcouncil/cli/commands/rollback.py +46 -35
  63. package/src/devcouncil/cli/commands/run.py +173 -8
  64. package/src/devcouncil/cli/commands/runs.py +298 -68
  65. package/src/devcouncil/cli/commands/scaffold.py +33 -12
  66. package/src/devcouncil/cli/commands/semantic.py +29 -14
  67. package/src/devcouncil/cli/commands/setup.py +103 -93
  68. package/src/devcouncil/cli/commands/shell.py +51 -42
  69. package/src/devcouncil/cli/commands/show.py +56 -42
  70. package/src/devcouncil/cli/commands/skills.py +29 -20
  71. package/src/devcouncil/cli/commands/status.py +80 -67
  72. package/src/devcouncil/cli/commands/task_gate.py +295 -0
  73. package/src/devcouncil/cli/commands/tasks.py +248 -19
  74. package/src/devcouncil/cli/commands/trace.py +14 -8
  75. package/src/devcouncil/cli/commands/verify.py +33 -8
  76. package/src/devcouncil/cli/commands/version.py +14 -6
  77. package/src/devcouncil/cli/commands/watch.py +49 -30
  78. package/src/devcouncil/cli/commands/watch_fs.py +30 -19
  79. package/src/devcouncil/cli/commands/wiki.py +278 -0
  80. package/src/devcouncil/cli/main.py +58 -1
  81. package/src/devcouncil/codeintel/__init__.py +16 -0
  82. package/src/devcouncil/codeintel/build_control.py +429 -0
  83. package/src/devcouncil/codeintel/build_worker.py +78 -0
  84. package/src/devcouncil/codeintel/debug/__init__.py +17 -0
  85. package/src/devcouncil/codeintel/debug/broker.py +114 -0
  86. package/src/devcouncil/codeintel/debug/broker_client.py +61 -0
  87. package/src/devcouncil/codeintel/debug/consent.py +36 -0
  88. package/src/devcouncil/codeintel/debug/discovery.py +132 -0
  89. package/src/devcouncil/codeintel/debug/fingerprint.py +85 -0
  90. package/src/devcouncil/codeintel/debug/protocol.py +259 -0
  91. package/src/devcouncil/codeintel/debug/python_trace_runner.py +81 -0
  92. package/src/devcouncil/codeintel/debug/session.py +238 -0
  93. package/src/devcouncil/codeintel/debug/tracing.py +201 -0
  94. package/src/devcouncil/codeintel/languages/__init__.py +17 -0
  95. package/src/devcouncil/codeintel/languages/generic_extractor.py +236 -0
  96. package/src/devcouncil/codeintel/languages/registry.py +149 -0
  97. package/src/devcouncil/codeintel/languages/workers.py +245 -0
  98. package/src/devcouncil/codeintel/query/__init__.py +5 -0
  99. package/src/devcouncil/codeintel/query/engine.py +289 -0
  100. package/src/devcouncil/codeintel/resolution/__init__.py +6 -0
  101. package/src/devcouncil/codeintel/resolution/abstract_state.py +301 -0
  102. package/src/devcouncil/codeintel/resolution/frameworks/__init__.py +33 -0
  103. package/src/devcouncil/codeintel/resolution/frameworks/base.py +46 -0
  104. package/src/devcouncil/codeintel/resolution/frameworks/di.py +56 -0
  105. package/src/devcouncil/codeintel/resolution/frameworks/events.py +45 -0
  106. package/src/devcouncil/codeintel/resolution/frameworks/routes.py +88 -0
  107. package/src/devcouncil/codeintel/resolution/semantic.py +887 -0
  108. package/src/devcouncil/codeintel/service.py +104 -0
  109. package/src/devcouncil/codeintel/store/__init__.py +15 -0
  110. package/src/devcouncil/codeintel/store/sqlite.py +1565 -0
  111. package/src/devcouncil/codeintel/sync/__init__.py +19 -0
  112. package/src/devcouncil/codeintel/sync/coordinator.py +430 -0
  113. package/src/devcouncil/codeintel/sync/incremental.py +484 -0
  114. package/src/devcouncil/codeintel/sync/lease.py +96 -0
  115. package/src/devcouncil/codeintel/sync/scope.py +98 -0
  116. package/src/devcouncil/council/__init__.py +4 -0
  117. package/src/devcouncil/council/prompts/__init__.py +4 -0
  118. package/src/devcouncil/domain/checkpoint_refs.py +17 -0
  119. package/src/devcouncil/domain/evidence.py +1 -0
  120. package/src/devcouncil/domain/gap.py +10 -0
  121. package/src/devcouncil/domain/requirement.py +5 -1
  122. package/src/devcouncil/domain/task.py +43 -2
  123. package/src/devcouncil/execution/checkpoints.py +25 -31
  124. package/src/devcouncil/execution/context_builder.py +15 -44
  125. package/src/devcouncil/execution/fs_watcher.py +64 -0
  126. package/src/devcouncil/execution/gated_write.py +203 -0
  127. package/src/devcouncil/execution/handoff.py +2 -1
  128. package/src/devcouncil/execution/hook_policy.py +19 -5
  129. package/src/devcouncil/execution/lease_ops.py +177 -0
  130. package/src/devcouncil/execution/lease_validation.py +71 -0
  131. package/src/devcouncil/execution/patch.py +3 -0
  132. package/src/devcouncil/execution/permissions.py +1 -0
  133. package/src/devcouncil/execution/policy_engine.py +205 -10
  134. package/src/devcouncil/execution/prompt_builder.py +278 -33
  135. package/src/devcouncil/execution/run_trace.py +356 -0
  136. package/src/devcouncil/execution/shell_session.py +46 -5
  137. package/src/devcouncil/execution/stop_gate.py +746 -0
  138. package/src/devcouncil/execution/stop_gate_history.py +113 -0
  139. package/src/devcouncil/execution/stop_gate_state.py +54 -0
  140. package/src/devcouncil/execution/stop_gate_verify_cache.py +69 -0
  141. package/src/devcouncil/execution/task_gate_ops.py +590 -0
  142. package/src/devcouncil/execution/task_runner.py +19 -0
  143. package/src/devcouncil/executors/advisor_tool.py +315 -0
  144. package/src/devcouncil/executors/agent_registry.py +125 -17
  145. package/src/devcouncil/executors/claude_sdk.py +376 -0
  146. package/src/devcouncil/executors/coding_cli.py +724 -25
  147. package/src/devcouncil/executors/mini_swe.py +50 -8
  148. package/src/devcouncil/executors/native/agent.py +224 -19
  149. package/src/devcouncil/executors/openhands.py +50 -8
  150. package/src/devcouncil/executors/transient_retry.py +99 -0
  151. package/src/devcouncil/gating/checks/clean_git.py +5 -2
  152. package/src/devcouncil/gating/checks/planned_files_check.py +38 -11
  153. package/src/devcouncil/gating/checks/secret_scan_check.py +2 -2
  154. package/src/devcouncil/gating/policy.py +46 -2
  155. package/src/devcouncil/indexing/ast_matcher.py +41 -4
  156. package/src/devcouncil/indexing/graph/__init__.py +78 -0
  157. package/src/devcouncil/indexing/graph/api_routes.py +522 -0
  158. package/src/devcouncil/indexing/graph/build.py +862 -0
  159. package/src/devcouncil/indexing/graph/cache.py +329 -0
  160. package/src/devcouncil/indexing/graph/communities.py +28 -0
  161. package/src/devcouncil/indexing/graph/cypher.py +107 -0
  162. package/src/devcouncil/indexing/graph/embeddings.py +194 -0
  163. package/src/devcouncil/indexing/graph/export.py +381 -0
  164. package/src/devcouncil/indexing/graph/export_links.py +81 -0
  165. package/src/devcouncil/indexing/graph/extract_python.py +307 -0
  166. package/src/devcouncil/indexing/graph/extract_ts.py +1205 -0
  167. package/src/devcouncil/indexing/graph/intel.py +668 -0
  168. package/src/devcouncil/indexing/graph/liveness.py +992 -0
  169. package/src/devcouncil/indexing/graph/okf_export.py +65 -0
  170. package/src/devcouncil/indexing/graph/pdg/__init__.py +67 -0
  171. package/src/devcouncil/indexing/graph/pdg/build.py +11 -0
  172. package/src/devcouncil/indexing/graph/pdg/cdg.py +41 -0
  173. package/src/devcouncil/indexing/graph/pdg/cfg.py +199 -0
  174. package/src/devcouncil/indexing/graph/pdg/query.py +21 -0
  175. package/src/devcouncil/indexing/graph/pdg/reaching_def.py +126 -0
  176. package/src/devcouncil/indexing/graph/pdg/schema.py +253 -0
  177. package/src/devcouncil/indexing/graph/pdg/taint.py +154 -0
  178. package/src/devcouncil/indexing/graph/query.py +302 -0
  179. package/src/devcouncil/indexing/graph/resolve.py +1020 -0
  180. package/src/devcouncil/indexing/graph/schema.py +103 -0
  181. package/src/devcouncil/indexing/graph_index.py +20 -29
  182. package/src/devcouncil/indexing/lsp.py +57 -25
  183. package/src/devcouncil/indexing/lsp_client.py +577 -0
  184. package/src/devcouncil/indexing/map_artifacts.py +355 -0
  185. package/src/devcouncil/indexing/map_refresh.py +141 -0
  186. package/src/devcouncil/indexing/repo_mapper.py +1509 -138
  187. package/src/devcouncil/indexing/semantic_index.py +12 -6
  188. package/src/devcouncil/indexing/subsystem_map.py +163 -0
  189. package/src/devcouncil/indexing/ts_imports.py +343 -0
  190. package/src/devcouncil/indexing/viz.py +960 -0
  191. package/src/devcouncil/indexing/walk.py +52 -0
  192. package/src/devcouncil/indexing/wiring.py +1776 -0
  193. package/src/devcouncil/integrations/actions.py +27 -4
  194. package/src/devcouncil/integrations/check.py +211 -16
  195. package/src/devcouncil/integrations/claude_assets.py +209 -12
  196. package/src/devcouncil/integrations/clients/__init__.py +1 -0
  197. package/src/devcouncil/integrations/clients/aider.py +52 -0
  198. package/src/devcouncil/integrations/clients/antigravity.py +87 -0
  199. package/src/devcouncil/integrations/clients/claude.py +339 -0
  200. package/src/devcouncil/integrations/clients/codex.py +39 -0
  201. package/src/devcouncil/integrations/clients/common.py +332 -0
  202. package/src/devcouncil/integrations/clients/cursor.py +164 -0
  203. package/src/devcouncil/integrations/clients/gemini.py +49 -0
  204. package/src/devcouncil/integrations/clients/grok.py +105 -0
  205. package/src/devcouncil/integrations/clients/hooks.py +500 -0
  206. package/src/devcouncil/integrations/clients/opencode.py +96 -0
  207. package/src/devcouncil/integrations/clients/warp.py +75 -0
  208. package/src/devcouncil/integrations/code_review_graph.py +2 -2
  209. package/src/devcouncil/integrations/github.py +73 -7
  210. package/src/devcouncil/integrations/integration_cli.py +197 -0
  211. package/src/devcouncil/integrations/mcp/handlers/__init__.py +1 -0
  212. package/src/devcouncil/integrations/mcp/handlers/ast_lsp.py +77 -0
  213. package/src/devcouncil/integrations/mcp/handlers/checkout.py +50 -0
  214. package/src/devcouncil/integrations/mcp/handlers/cli_gate.py +43 -0
  215. package/src/devcouncil/integrations/mcp/handlers/codeintel.py +182 -0
  216. package/src/devcouncil/integrations/mcp/handlers/debug.py +236 -0
  217. package/src/devcouncil/integrations/mcp/handlers/evidence.py +70 -0
  218. package/src/devcouncil/integrations/mcp/handlers/git.py +99 -0
  219. package/src/devcouncil/integrations/mcp/handlers/graph.py +36 -0
  220. package/src/devcouncil/integrations/mcp/handlers/handoff.py +53 -0
  221. package/src/devcouncil/integrations/mcp/handlers/knowledge.py +30 -0
  222. package/src/devcouncil/integrations/mcp/handlers/lease.py +70 -0
  223. package/src/devcouncil/integrations/mcp/handlers/live.py +108 -0
  224. package/src/devcouncil/integrations/mcp/handlers/map.py +676 -0
  225. package/src/devcouncil/integrations/mcp/handlers/next_task.py +35 -0
  226. package/src/devcouncil/integrations/mcp/handlers/policy.py +80 -0
  227. package/src/devcouncil/integrations/mcp/handlers/prompts.py +168 -0
  228. package/src/devcouncil/integrations/mcp/handlers/provenance.py +101 -0
  229. package/src/devcouncil/integrations/mcp/handlers/read.py +74 -0
  230. package/src/devcouncil/integrations/mcp/handlers/router_cache.py +53 -0
  231. package/src/devcouncil/integrations/mcp/handlers/run.py +53 -0
  232. package/src/devcouncil/integrations/mcp/handlers/runs.py +84 -0
  233. package/src/devcouncil/integrations/mcp/handlers/scope.py +56 -0
  234. package/src/devcouncil/integrations/mcp/handlers/status.py +114 -0
  235. package/src/devcouncil/integrations/mcp/handlers/task.py +100 -0
  236. package/src/devcouncil/integrations/mcp/handlers/tool_specs.py +904 -0
  237. package/src/devcouncil/integrations/mcp/handlers/trace.py +65 -0
  238. package/src/devcouncil/integrations/mcp/handlers/verify.py +45 -0
  239. package/src/devcouncil/integrations/mcp/handlers/wiki.py +60 -0
  240. package/src/devcouncil/integrations/mcp/handlers/write.py +69 -0
  241. package/src/devcouncil/integrations/mcp/server.py +265 -2391
  242. package/src/devcouncil/integrations/mcp/util.py +303 -0
  243. package/src/devcouncil/integrations/setup.py +152 -0
  244. package/src/devcouncil/knowledge/fetch.py +4 -0
  245. package/src/devcouncil/knowledge/knowledge_select.py +38 -0
  246. package/src/devcouncil/knowledge/okf.py +2 -1
  247. package/src/devcouncil/knowledge/resource_discovery.py +40 -0
  248. package/src/devcouncil/knowledge/wiki.py +643 -0
  249. package/src/devcouncil/knowledge/wiki_read.py +87 -0
  250. package/src/devcouncil/live/cards.py +7 -7
  251. package/src/devcouncil/live/models.py +4 -1
  252. package/src/devcouncil/live/reviewer.py +90 -11
  253. package/src/devcouncil/live/signals.py +4 -2
  254. package/src/devcouncil/live/tasks.py +12 -3
  255. package/src/devcouncil/live/transcripts.py +69 -2
  256. package/src/devcouncil/llm/cache.py +5 -6
  257. package/src/devcouncil/llm/model_defaults.yaml +10 -10
  258. package/src/devcouncil/llm/provider.py +647 -73
  259. package/src/devcouncil/llm/router.py +271 -46
  260. package/src/devcouncil/llm/semantic_bridge.py +614 -0
  261. package/src/devcouncil/optimization/gepa_agent.py +6 -4
  262. package/src/devcouncil/optimization/skillopt.py +9 -5
  263. package/src/devcouncil/planning/arbiter_service.py +12 -3
  264. package/src/devcouncil/planning/correction_manifest.py +107 -10
  265. package/src/devcouncil/planning/plan_difficulty.py +69 -0
  266. package/src/devcouncil/planning/plan_service.py +5 -2
  267. package/src/devcouncil/planning/planned_files_reconcile.py +191 -0
  268. package/src/devcouncil/planning/prompt_enhancer_service.py +6 -5
  269. package/src/devcouncil/planning/question_conversion.py +56 -0
  270. package/src/devcouncil/planning/spec_service.py +9 -3
  271. package/src/devcouncil/repo/ci_scaffold.py +197 -1
  272. package/src/devcouncil/repo/gitignore.py +1 -2
  273. package/src/devcouncil/reporting/evidence_export.py +124 -0
  274. package/src/devcouncil/reporting/evidence_html.py +210 -0
  275. package/src/devcouncil/reporting/json_report.py +16 -12
  276. package/src/devcouncil/reporting/markdown_report.py +38 -9
  277. package/src/devcouncil/reporting/mcp_resources.py +142 -0
  278. package/src/devcouncil/reporting/report_builder.py +40 -4
  279. package/src/devcouncil/reporting/task_provenance.py +42 -0
  280. package/src/devcouncil/reporting/verdict.py +75 -0
  281. package/src/devcouncil/skills/library/README.md +1 -0
  282. package/src/devcouncil/skills/library/devcouncil-hero-loop.md +109 -0
  283. package/src/devcouncil/skills/library/devcouncil-verification.md +109 -0
  284. package/src/devcouncil/skills/library/devcouncil.md +93 -0
  285. package/src/devcouncil/skills/registry.py +43 -12
  286. package/src/devcouncil/storage/db.py +57 -11
  287. package/src/devcouncil/storage/models.py +6 -0
  288. package/src/devcouncil/storage/native.py +5 -3
  289. package/src/devcouncil/storage/repositories.py +50 -18
  290. package/src/devcouncil/telemetry/context.py +28 -0
  291. package/src/devcouncil/telemetry/cost.py +4 -5
  292. package/src/devcouncil/telemetry/logging_setup.py +78 -11
  293. package/src/devcouncil/telemetry/model_pricing.yaml +7 -0
  294. package/src/devcouncil/telemetry/stages.py +27 -2
  295. package/src/devcouncil/telemetry/tracker.py +50 -13
  296. package/src/devcouncil/ui/dashboard.py +120 -8
  297. package/src/devcouncil/utils/fsio.py +58 -0
  298. package/src/devcouncil/utils/git_snapshot.py +112 -0
  299. package/src/devcouncil/utils/json_persist.py +53 -0
  300. package/src/devcouncil/utils/proc.py +89 -0
  301. package/src/devcouncil/verification/acceptance_compiler.py +36 -13
  302. package/src/devcouncil/verification/ad_hoc_check.py +95 -3
  303. package/src/devcouncil/verification/checks/__init__.py +41 -0
  304. package/src/devcouncil/verification/checks/acceptance.py +39 -0
  305. package/src/devcouncil/verification/checks/acceptance_corpus.py +194 -0
  306. package/src/devcouncil/verification/checks/acceptance_evidence.py +239 -0
  307. package/src/devcouncil/verification/checks/command_evidence.py +148 -0
  308. package/src/devcouncil/verification/checks/compiled_acceptance.py +179 -0
  309. package/src/devcouncil/verification/checks/corpus_stale.py +124 -0
  310. package/src/devcouncil/verification/checks/corpus_verification.py +9 -0
  311. package/src/devcouncil/verification/checks/dead_symbols.py +360 -0
  312. package/src/devcouncil/verification/checks/diff_coverage_gate.py +101 -0
  313. package/src/devcouncil/verification/checks/doc_code_ref.py +79 -0
  314. package/src/devcouncil/verification/checks/liveness_ratchet.py +336 -0
  315. package/src/devcouncil/verification/checks/orphan_diff.py +104 -0
  316. package/src/devcouncil/verification/checks/planned_files.py +98 -0
  317. package/src/devcouncil/verification/checks/semantic_diff.py +241 -0
  318. package/src/devcouncil/verification/checks/stale_map.py +80 -0
  319. package/src/devcouncil/verification/checks/stub_scan.py +71 -0
  320. package/src/devcouncil/verification/checks/subsystem_boundary.py +103 -0
  321. package/src/devcouncil/verification/checks/wiring.py +216 -0
  322. package/src/devcouncil/verification/claims/__init__.py +23 -0
  323. package/src/devcouncil/verification/claims/checks.py +395 -0
  324. package/src/devcouncil/verification/claims/mapper.py +168 -0
  325. package/src/devcouncil/verification/claims/models.py +39 -0
  326. package/src/devcouncil/verification/claims/transcript.py +92 -0
  327. package/src/devcouncil/verification/claims/verdict.py +88 -0
  328. package/src/devcouncil/verification/command_evidence.py +170 -0
  329. package/src/devcouncil/verification/command_malformation.py +147 -0
  330. package/src/devcouncil/verification/command_runner.py +164 -0
  331. package/src/devcouncil/verification/coverage_measurement.py +292 -0
  332. package/src/devcouncil/verification/diff_coverage.py +151 -0
  333. package/src/devcouncil/verification/difficulty.py +296 -0
  334. package/src/devcouncil/verification/effort_heuristics.py +178 -0
  335. package/src/devcouncil/verification/gap_ids.py +63 -0
  336. package/src/devcouncil/verification/gate_cache.py +194 -0
  337. package/src/devcouncil/verification/gate_selector.py +344 -0
  338. package/src/devcouncil/verification/git_diff_fallback.py +272 -0
  339. package/src/devcouncil/verification/implementation_reviewer.py +13 -0
  340. package/src/devcouncil/verification/incremental_check.py +241 -0
  341. package/src/devcouncil/verification/next_actions.py +60 -1
  342. package/src/devcouncil/verification/rigor_analytics.py +130 -0
  343. package/src/devcouncil/verification/sandbox.py +38 -11
  344. package/src/devcouncil/verification/stub_detector.py +369 -0
  345. package/src/devcouncil/verification/test_resolver.py +67 -1
  346. package/src/devcouncil/verification/verifier.py +137 -1666
  347. package/src/devcouncil/verification/verify_orchestration.py +610 -0
  348. package/src/devcouncil/verification/verify_setup.py +176 -0
  349. package/src/devcouncil/verification/wiki_refresh.py +208 -0
  350. package/src/semantic_layer/__init__.py +58 -0
  351. package/src/semantic_layer/benchmark.py +75 -0
  352. package/src/semantic_layer/cache.py +290 -0
  353. package/src/semantic_layer/compressor.py +137 -0
  354. package/src/semantic_layer/config.py +75 -0
  355. package/src/semantic_layer/embeddings.py +69 -0
  356. package/src/semantic_layer/llm_backends.py +99 -0
  357. package/src/semantic_layer/pipeline.py +111 -0
  358. package/src/semantic_layer/router.py +128 -0
  359. package/src/semantic_layer/tuner.py +72 -0
  360. package/uv.lock +973 -9
  361. package/src/devcouncil/artifacts/migrations.py +0 -20
  362. package/src/devcouncil/artifacts/schemas.py +0 -23
  363. package/src/devcouncil/artifacts/serializer.py +0 -21
  364. package/src/devcouncil/integrations/gitnexus.py +0 -70
  365. package/src/devcouncil/integrations/graphify.py +0 -34
@@ -1,11 +1,14 @@
1
+ import logging
1
2
  import typer
2
3
  from pathlib import Path
3
4
  from rich.console import Console
4
5
 
5
6
  from devcouncil.execution.checkpoints import CheckpointService
7
+ from devcouncil.telemetry.stages import log_stage, log_step
6
8
 
7
9
  app = typer.Typer()
8
10
  console = Console()
11
+ logger = logging.getLogger(__name__)
9
12
 
10
13
  @app.callback(invoke_without_command=True)
11
14
  def rollback(
@@ -20,40 +23,48 @@ def rollback(
20
23
  return
21
24
 
22
25
  root = project_root.expanduser().resolve()
23
- checkpoint_dir = root / ".devcouncil" / "checkpoints"
24
- checkpoint_file = checkpoint_dir / f"{task_id}-before.patch"
25
- after_patch = checkpoint_dir / f"{task_id}-after.patch"
26
- service = CheckpointService(root)
27
-
28
- if not checkpoint_file.exists() and not after_patch.exists():
29
- before_ref = CheckpointService.REF_BEFORE.format(task_id=task_id)
30
- after_ref = CheckpointService.REF_AFTER.format(task_id=task_id)
31
- if not service._ref_exists(before_ref) and not service._ref_exists(after_ref):
32
- console.print(
33
- f"[red]No checkpoint found for task {task_id}. Expected {after_patch} "
34
- f"or {checkpoint_file}.[/red]"
35
- )
26
+ from devcouncil.telemetry.logging_setup import set_log_dir
27
+ set_log_dir(root)
28
+ logger.info("dev rollback: task=%s", task_id)
29
+
30
+ with log_stage("rollback", project_root=root, task_id=task_id):
31
+ log_step("rollback/1: locating checkpoint", project_root=root, task_id=task_id, trace=True)
32
+ checkpoint_dir = root / ".devcouncil" / "checkpoints"
33
+ checkpoint_file = checkpoint_dir / f"{task_id}-before.patch"
34
+ after_patch = checkpoint_dir / f"{task_id}-after.patch"
35
+ service = CheckpointService(root)
36
+
37
+ if not checkpoint_file.exists() and not after_patch.exists():
38
+ before_ref = CheckpointService.REF_BEFORE.format(task_id=task_id)
39
+ after_ref = CheckpointService.REF_AFTER.format(task_id=task_id)
40
+ if not service._ref_exists(before_ref) and not service._ref_exists(after_ref):
41
+ console.print(
42
+ f"[red]No checkpoint found for task {task_id}. Expected {after_patch} "
43
+ f"or {checkpoint_file}.[/red]"
44
+ )
45
+ raise typer.Exit(code=1)
46
+
47
+ console.print(f"Rolling back task [bold]{task_id}[/bold]...")
48
+ log_step("rollback/2: reverting checkpoint", project_root=root, task_id=task_id)
49
+ result = service.rollback(task_id)
50
+ if "failed" in result.message.lower() or result.message.startswith("No checkpoint"):
51
+ console.print(f"[yellow]{result.message}[/yellow]")
52
+ if checkpoint_file.exists():
53
+ console.print(
54
+ f"The before-patch at {checkpoint_file} captured the state before the task ran.\n"
55
+ f"To manually reset:\n"
56
+ f" 1. [bold]git stash[/bold] (if you want to keep current changes)\n"
57
+ f" 2. [bold]git checkout -- .[/bold] (discard working tree changes)\n"
58
+ f" 3. [bold]git apply {checkpoint_file}[/bold] (restore pre-task state)"
59
+ )
60
+ elif after_patch.exists():
61
+ console.print(
62
+ f"Only the after-patch at {after_patch} exists (it captured the task's changes).\n"
63
+ f"To manually revert those changes from the working tree:\n"
64
+ f" 1. [bold]git apply --stat {after_patch}[/bold] (inspect what the task changed)\n"
65
+ f" 2. [bold]git apply -R {after_patch}[/bold] (reverse-apply the task's changes)"
66
+ )
36
67
  raise typer.Exit(code=1)
37
68
 
38
- console.print(f"Rolling back task [bold]{task_id}[/bold]...")
39
- result = service.rollback(task_id)
40
- if "failed" in result.message.lower() or result.message.startswith("No checkpoint"):
41
- console.print(f"[yellow]{result.message}[/yellow]")
42
- if checkpoint_file.exists():
43
- console.print(
44
- f"The before-patch at {checkpoint_file} captured the state before the task ran.\n"
45
- f"To manually reset:\n"
46
- f" 1. [bold]git stash[/bold] (if you want to keep current changes)\n"
47
- f" 2. [bold]git checkout -- .[/bold] (discard working tree changes)\n"
48
- f" 3. [bold]git apply {checkpoint_file}[/bold] (restore pre-task state)"
49
- )
50
- elif after_patch.exists():
51
- console.print(
52
- f"Only the after-patch at {after_patch} exists (it captured the task's changes).\n"
53
- f"To manually revert those changes from the working tree:\n"
54
- f" 1. [bold]git apply --stat {after_patch}[/bold] (inspect what the task changed)\n"
55
- f" 2. [bold]git apply -R {after_patch}[/bold] (reverse-apply the task's changes)"
56
- )
57
- raise typer.Exit(code=1)
58
-
59
- console.print(f"[green]Successfully rolled back task {task_id}.[/green] {result.message}")
69
+ console.print(f"[green]Successfully rolled back task {task_id}.[/green] {result.message}")
70
+ log_step("rollback/complete", project_root=root, task_id=task_id, trace=True)
@@ -10,6 +10,7 @@ from devcouncil.executors.coding_cli import CodingCliExecutor
10
10
  from devcouncil.executors.agent_registry import (
11
11
  AGENT_ALIASES,
12
12
  BUILTIN_CODING_EXECUTOR_NAMES,
13
+ list_run_executor_names,
13
14
  load_cli_agent_specs,
14
15
  )
15
16
  from devcouncil.executors.native.agent import NativeAgent
@@ -17,11 +18,14 @@ from devcouncil.llm.provider import create_provider, validate_model_provider
17
18
  from devcouncil.llm.router import ModelRouter
18
19
  from devcouncil.app.config import load_config, get_api_key
19
20
  from devcouncil.domain.evidence import CommandResult, DiffEvidence, TestEvidence
21
+ from devcouncil.domain.task import Task
22
+ from devcouncil.domain.requirement import Requirement
20
23
  from devcouncil.storage.repositories import GapRepository, EvidenceRepository, StateRepository
21
24
  from devcouncil.verification.verifier import Verifier
22
25
  from devcouncil.app.state_machine import ProjectPhase
23
26
  from devcouncil.cli.commands.init import initialize_project
24
27
  from devcouncil.telemetry.traces import TraceLogger
28
+ from devcouncil.telemetry.stages import log_stage, log_step
25
29
 
26
30
  console = Console()
27
31
  logger = logging.getLogger(__name__)
@@ -39,14 +43,59 @@ def _current_changed_files(project_root: Path = Path(".")) -> list[str]:
39
43
 
40
44
  return Verifier(project_root).get_changed_files()
41
45
 
46
+
47
+ def _verify_executor_output_if_present(
48
+ session,
49
+ task,
50
+ reqs,
51
+ *,
52
+ root: Path,
53
+ executor_label: str,
54
+ cli_client: str | None = None,
55
+ cli_executor=None,
56
+ ) -> bool:
57
+ """Run verification when the executor failed but left implementable changes."""
58
+ if not _current_changed_files(root):
59
+ return False
60
+ console.print(
61
+ f"[yellow]{executor_label} failed, but the workspace has changes — "
62
+ f"verifying {task.id} against them anyway.[/yellow]"
63
+ )
64
+ verified = _verify_after_execution(
65
+ session, task, reqs, router=_build_verification_router(root), project_root=root
66
+ )
67
+ _record_project_phase(
68
+ session,
69
+ ProjectPhase.TASK_VERIFIED if verified else ProjectPhase.TASK_BLOCKED,
70
+ )
71
+ TaskRepository(session).save(task)
72
+ if cli_client:
73
+ _record_agent_verification(
74
+ root,
75
+ task.id,
76
+ cli_client,
77
+ getattr(cli_executor, "last_run_id", None) if cli_executor else None,
78
+ verified,
79
+ )
80
+ _run_live_review_after_execution(root, cli_client, task.id)
81
+ _log_exec_outcome(executor_label, task.id, verified=verified)
82
+ if verified:
83
+ console.print(f"\n[green]{executor_label} left verifiable work; {task.id} verified.[/green]")
84
+ else:
85
+ console.print(
86
+ f"\n[yellow]{executor_label} left changes, but {task.id} is blocked by verification gaps.[/yellow]"
87
+ )
88
+ return True
89
+
42
90
  def _capture_after_patch(task_id: str, project_root: Path = Path(".")):
43
91
  """Capture the diff after task execution for use by rollback."""
44
92
  try:
45
93
  from devcouncil.execution.checkpoints import CheckpointService
46
94
 
47
95
  CheckpointService(project_root).create_after(task_id)
48
- except Exception:
49
- pass # Non-critical — don't block execution
96
+ except Exception as e:
97
+ # Non-critical — don't block execution, but a missing after checkpoint breaks rollback.
98
+ logger.warning("Failed to capture after checkpoint for %s: %s", task_id, e)
50
99
 
51
100
  def _capture_before_snapshot(task_id: str, project_root: Path = Path(".")):
52
101
  from devcouncil.execution.checkpoints import CheckpointService
@@ -56,11 +105,22 @@ def _capture_before_snapshot(task_id: str, project_root: Path = Path(".")):
56
105
  def _record_project_phase(session, phase: ProjectPhase):
57
106
  StateRepository(session).record_phase(phase.value)
58
107
 
59
- def _verify_after_execution(session, task, reqs, router=None, project_root: Path = Path(".")) -> bool:
108
+ def _verify_after_execution(
109
+ session,
110
+ task: Task,
111
+ reqs: list[Requirement],
112
+ router=None,
113
+ project_root: Path = Path("."),
114
+ ) -> bool:
60
115
  """Run deterministic verification after an automated executor finishes."""
61
116
  import asyncio
62
117
 
63
118
  logger.info("Verifying task %s (router=%s)", task.id, "yes" if router else "no")
119
+ log_step(
120
+ "run/verify: verifying executor output",
121
+ project_root=project_root,
122
+ task_id=task.id,
123
+ )
64
124
  verifier = Verifier(project_root, router=router)
65
125
  gaps, evidence = asyncio.run(verifier.verify_task(task, reqs))
66
126
  blocking = [g for g in gaps if g.blocking]
@@ -148,13 +208,14 @@ def _run_live_review_after_execution(project_root: Path, client: str, task_id: s
148
208
  )
149
209
  from devcouncil.live.transcripts import latest_assistant_turn
150
210
 
151
- transcript = _resolve_transcript(project_root, client, latest=True)
211
+ transcript = _resolve_transcript(project_root, client, latest=True, task_id=task_id)
152
212
  if transcript is None or not transcript.exists():
153
213
  return
154
214
  turn = latest_assistant_turn(transcript, client=client)
155
215
  if turn is None:
156
216
  return
157
217
  card = asyncio.run(_review_turn(turn, project_root, client, use_llm=True, task_id=task_id))
218
+ card = card.model_copy(update={"task_id": task_id, "blocks_gate": False})
158
219
  saved_path, duplicate = _save_card_once(project_root, card, persist=True, force=False)
159
220
  if saved_path:
160
221
  _log_card_reviewed(project_root, card, saved_path, duplicate=duplicate, source="e2e")
@@ -188,8 +249,8 @@ def run(
188
249
  "--executor",
189
250
  "-e",
190
251
  help=(
191
- "Executor to use (manual, mini, openhands, native-preview, "
192
- "codex, gemini, claude, opencode, antigravity, warp, cursor, aider, "
252
+ "Executor to use (manual, mini, openhands, native-preview, claude-sdk, "
253
+ "codex, gemini, claude, opencode, antigravity, warp, cursor, grok, aider, "
193
254
  "copilot, goose, amp, qwen, crush, or a configured agent)"
194
255
  ),
195
256
  ),
@@ -214,6 +275,11 @@ def run(
214
275
  console.print("[red]DevCouncil state is unavailable in this directory.[/red]")
215
276
  return
216
277
 
278
+ with log_stage("run", project_root=root, task_id=task_id, executor=executor):
279
+ _run_task_body(root, task_id, executor, profile, stream, db)
280
+
281
+
282
+ def _run_task_body(root, task_id, executor, profile, stream, db):
217
283
  with db.get_session() as session:
218
284
  task_repo = TaskRepository(session)
219
285
  task = task_repo.get_by_id(task_id)
@@ -237,6 +303,7 @@ def run(
237
303
 
238
304
  console.print(f"Running task [bold]{task_id}[/bold] using [bold]{executor}[/bold] executor...")
239
305
 
306
+ log_step("run/1: creating git checkpoint", project_root=root, task_id=task_id)
240
307
  # 1. Create Git checkpoint
241
308
  try:
242
309
  from devcouncil.execution.checkpoints import CheckpointService
@@ -250,6 +317,38 @@ def run(
250
317
  console.print(f"[yellow]Warning: Failed to create git checkpoint: {e}[/yellow]")
251
318
 
252
319
  executor = executor.strip().lower().replace("_", "-")
320
+ if executor == "claude-sdk":
321
+ # Preflight the optional SDK BEFORE mutating any state: previously a
322
+ # missing claude-agent-sdk was only discovered inside run_task, after
323
+ # planning had already spent its budget — every such run failed at the
324
+ # finish line with "SDK is not installed" (observed repeatedly in logs).
325
+ # Degrade to the Claude Code CLI executor when it is available (same
326
+ # agent, hook-based gating instead of in-process gating); otherwise
327
+ # fail fast with the actionable install hint.
328
+ import importlib.util
329
+ import shutil as _shutil
330
+
331
+ if importlib.util.find_spec("claude_agent_sdk") is None:
332
+ if _shutil.which("claude"):
333
+ logger.warning(
334
+ "claude-agent-sdk is not installed; falling back to the "
335
+ "'claude' CLI executor for %s (install claude-agent-sdk "
336
+ "to restore in-process tool gating).", task_id,
337
+ )
338
+ console.print(
339
+ "[yellow]claude-agent-sdk is not installed — falling back to the "
340
+ "'claude' CLI executor. Install it with "
341
+ "`pip install claude-agent-sdk` to use --executor claude-sdk.[/yellow]"
342
+ )
343
+ executor = "claude"
344
+ else:
345
+ logger.error("claude-sdk executor unavailable for %s: claude-agent-sdk not installed", task_id)
346
+ console.print(
347
+ "[red]The Claude Agent SDK is not installed and no `claude` CLI was found. "
348
+ "Install the SDK with `pip install claude-agent-sdk`, or use another "
349
+ "executor (e.g. --executor claude).[/red]"
350
+ )
351
+ return
253
352
  custom_agents = _custom_cli_agents(root)
254
353
  if executor not in CODING_EXECUTORS and executor not in custom_agents:
255
354
  ignored = [flag for flag, value in (("--profile", profile), ("--stream", stream)) if value]
@@ -266,6 +365,12 @@ def run(
266
365
  console.print("Use 'dev prompt TASK-ID' to get the prompt for this task.")
267
366
  console.print("When finished, use 'dev verify TASK-ID' to check the results.")
268
367
  elif executor in CODING_EXECUTORS or executor in custom_agents:
368
+ log_step(
369
+ f"run/2: executing with {executor}",
370
+ project_root=root,
371
+ task_id=task_id,
372
+ executor=executor,
373
+ )
269
374
  _record_project_phase(session, ProjectPhase.TASK_EXECUTING)
270
375
  req_repo = RequirementRepository(session)
271
376
  reqs = req_repo.get_all()
@@ -304,7 +409,18 @@ def run(
304
409
  else:
305
410
  logger.error("%s failed to start or execute for %s: %s", executor, task_id, exec_result.message)
306
411
  console.print(f"\n[red]{executor.upper()} failed to start or execute: {exec_result.message}[/red]")
412
+ if not _verify_executor_output_if_present(
413
+ session,
414
+ task,
415
+ reqs,
416
+ root=root,
417
+ executor_label=executor.upper(),
418
+ cli_client=cli_client,
419
+ cli_executor=cli_executor,
420
+ ):
421
+ task_repo.save(task)
307
422
  elif executor == "mini":
423
+ log_step("run/2: executing with mini-SWE-agent", project_root=root, task_id=task_id)
308
424
  _record_project_phase(session, ProjectPhase.TASK_EXECUTING)
309
425
  req_repo = RequirementRepository(session)
310
426
  reqs = req_repo.get_all()
@@ -330,6 +446,7 @@ def run(
330
446
  logger.error("mini-SWE-agent failed to start or execute for %s", task_id)
331
447
  console.print("\n[red]mini-SWE-agent failed to start or execute.[/red]")
332
448
  elif executor == "openhands":
449
+ log_step("run/2: executing with OpenHands", project_root=root, task_id=task_id)
333
450
  _record_project_phase(session, ProjectPhase.TASK_EXECUTING)
334
451
  req_repo = RequirementRepository(session)
335
452
  reqs = req_repo.get_all()
@@ -354,7 +471,52 @@ def run(
354
471
  else:
355
472
  logger.error("OpenHands failed to start or execute for %s", task_id)
356
473
  console.print("\n[red]OpenHands failed to start or execute.[/red]")
474
+ elif executor == "claude-sdk":
475
+ log_step("run/2: executing with claude-sdk", project_root=root, task_id=task_id)
476
+ # In-process Claude Agent SDK executor: every tool call is gated live against
477
+ # this task's scope, so out-of-scope writes/commands are denied before they land.
478
+ from devcouncil.executors.claude_sdk import ClaudeSdkExecutor
479
+ from devcouncil.executors.agent_registry import load_agent_profiles
480
+
481
+ _record_project_phase(session, ProjectPhase.TASK_EXECUTING)
482
+ req_repo = RequirementRepository(session)
483
+ reqs = req_repo.get_all()
484
+ # Honor --profile for model and env overrides (e.g. ANTHROPIC_BASE_URL to
485
+ # target an alternative Anthropic-compatible endpoint). permission_mode is
486
+ # deliberately NOT taken from the profile: the SDK executor's containment
487
+ # relies on "default" routing every tool call through can_use_tool; a
488
+ # profile's acceptEdits/auto would silently bypass the live scope gate.
489
+ sdk_profile = load_agent_profiles(root).get(profile or "default")
490
+ sdk_executor = ClaudeSdkExecutor(
491
+ root,
492
+ active_task=task,
493
+ model=(sdk_profile.model if sdk_profile else None),
494
+ advisor_model=(sdk_profile.advisor_model if sdk_profile else None),
495
+ env=(dict(sdk_profile.env) if sdk_profile and sdk_profile.env else None),
496
+ )
497
+ exec_result = sdk_executor.run_task(task, reqs)
498
+ _capture_after_patch(task_id, root)
499
+ if exec_result.success:
500
+ _record_project_phase(session, ProjectPhase.TASK_VERIFYING)
501
+ verified = _verify_after_execution(
502
+ session, task, reqs, router=_build_verification_router(root), project_root=root
503
+ )
504
+ _record_project_phase(
505
+ session,
506
+ ProjectPhase.TASK_VERIFIED if verified else ProjectPhase.TASK_BLOCKED,
507
+ )
508
+ task_repo.save(task)
509
+ _run_live_review_after_execution(root, "claude", task.id)
510
+ _log_exec_outcome("claude-sdk", task_id, verified=verified)
511
+ if verified:
512
+ console.print(f"\n[green]claude-sdk finished and task {task_id} verified.[/green]")
513
+ else:
514
+ console.print(f"\n[yellow]claude-sdk finished, but task {task_id} is blocked by verification gaps.[/yellow]")
515
+ else:
516
+ logger.error("claude-sdk failed to start or execute for %s: %s", task_id, exec_result.message)
517
+ console.print(f"\n[red]claude-sdk failed to start or execute: {exec_result.message}[/red]")
357
518
  elif executor in {"native", "native-preview"}:
519
+ log_step("run/2: executing with native agent", project_root=root, task_id=task_id)
358
520
  # Load config for model routing and permissions
359
521
  try:
360
522
  config = load_config(root)
@@ -406,5 +568,8 @@ def run(
406
568
  logger.error("Native agent failed during execution for %s", task_id)
407
569
  console.print("\n[red]Native agent failed during execution.[/red]")
408
570
  else:
409
- logger.error("Executor %r not implemented", executor)
410
- console.print(f"[red]Executor {executor} not yet implemented.[/red]")
571
+ available = ", ".join(list_run_executor_names(root))
572
+ logger.error("Executor %r not recognized; available: %s", executor, available)
573
+ console.print(f"[red]Unknown executor {executor!r}.[/red]")
574
+ console.print(f"[dim]Available executors: {available}[/dim]")
575
+ log_step("run/complete", project_root=root, task_id=task_id, executor=executor, trace=True)