devcouncil 0.3.1 → 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
@@ -2,31 +2,58 @@ from devcouncil.domain.task import Task
2
2
  from devcouncil.domain.gap import Gap
3
3
 
4
4
  class PlannedFilesCheck:
5
- """Ensures a task has legitimate files planned for modification."""
6
-
5
+ """Ensures a task has legitimate files planned for modification.
6
+
7
+ Calibration note: weak planner models routinely emit auxiliary process tasks
8
+ ("run linter checks", "verify the suite passes") that declare commands or
9
+ expected tests but no planned files. Hard-blocking those stalls the whole run
10
+ and surfaces as a false ``blocked`` verdict on correct code (seen directly in
11
+ the benchmark). A task that declares NOTHING actionable (no files, no commands,
12
+ no tests) still blocks — an executor genuinely cannot do anything with it —
13
+ but a command/verification-only task proceeds with an advisory, and is judged
14
+ on its actual output by the verify gate like any other task.
15
+ """
16
+
7
17
  def check(self, task: Task) -> list[Gap]:
8
18
  gaps = []
19
+ has_runnable_work = bool(task.allowed_commands or task.expected_tests)
9
20
  if not task.planned_files:
10
21
  gaps.append(Gap(
11
22
  id=f"GAP-{task.id}-NO-FILES",
12
- severity="high",
23
+ severity="high" if not has_runnable_work else "medium",
13
24
  gap_type="task_not_implemented",
14
25
  task_id=task.id,
15
- description=f"Task {task.id} has no planned files. Agents won't know where to write code.",
16
- recommended_fix="Update the task to include at least one planned file path.",
17
- blocking=True
26
+ description=(
27
+ f"Task {task.id} has no planned files. Agents won't know where to write code."
28
+ if not has_runnable_work else
29
+ f"Task {task.id} has no planned files but declares commands/tests; "
30
+ "treating it as a command/verification-only task (advisory)."
31
+ ),
32
+ recommended_fix=(
33
+ "Update the task to include at least one planned file path."
34
+ if not has_runnable_work else
35
+ "Add planned files if this task is meant to change code; otherwise this is fine."
36
+ ),
37
+ blocking=not has_runnable_work,
18
38
  ))
19
-
39
+
20
40
  has_modify = any(pf.allowed_change in ["create", "modify", "delete"] for pf in task.planned_files)
21
41
  if task.planned_files and not has_modify:
42
+ # Read-only-only tasks are analysis tasks: they can still execute (read code,
43
+ # run commands) and are already surfaced as advisory at PLAN time by the
44
+ # plan-approval gate. Blocking them at readiness contradicted that and
45
+ # stalled runs whose planner emitted a legitimate analysis step.
22
46
  gaps.append(Gap(
23
47
  id=f"GAP-{task.id}-READ-ONLY",
24
48
  severity="medium",
25
49
  gap_type="task_not_implemented",
26
50
  task_id=task.id,
27
- description=f"Task {task.id} only has read-only files. No changes can be made.",
28
- recommended_fix="Grant 'modify' or 'create' permissions to at least one file.",
29
- blocking=True
51
+ description=(
52
+ f"Task {task.id} only has read-only files; it can analyze but not change code. "
53
+ "Expected only if this is an analysis-only task."
54
+ ),
55
+ recommended_fix="Grant 'modify' or 'create' permissions to at least one file if this task should write code.",
56
+ blocking=False,
30
57
  ))
31
-
58
+
32
59
  return gaps
@@ -36,8 +36,8 @@ class SecretScanner:
36
36
  if pattern.search(line):
37
37
  counter += 1
38
38
  logger.warning(
39
- "Potential %s secret detected in %s:%d (task %s)",
40
- key_type, current_file, new_line_no, task_id,
39
+ "Potential sensitive pattern detected in %s:%d (task %s)",
40
+ current_file, new_line_no, task_id,
41
41
  )
42
42
  gaps.append(Gap(
43
43
  id=f"GAP-{task_id}-SECRET-{key_type.upper()}-{new_line_no}-{counter}",
@@ -12,10 +12,32 @@ from devcouncil.domain.critique import CritiqueFinding
12
12
  from devcouncil.gating.checks.requirement_coverage import RequirementCoverageCheck
13
13
  from devcouncil.gating.checks.planned_files_check import PlannedFilesCheck
14
14
  from devcouncil.gating.checks.clean_git import CleanGitCheck
15
+ from devcouncil.telemetry.stages import log_step
15
16
 
16
17
  logger = logging.getLogger(__name__)
17
18
 
18
19
 
20
+ class MapPolicyError(Exception):
21
+ """Raised when repository mapping validation fails (fail-closed)."""
22
+
23
+
24
+ class FailClosedMapPolicy:
25
+ """Fail-closed map policy: block execution when mapping is missing or invalid."""
26
+
27
+ def validate_repo_map(self, project_root: Path) -> None:
28
+ """Ensure the on-disk repo map exists and is readable (fail-closed)."""
29
+ map_path = project_root / ".devcouncil" / "repo_map.json"
30
+ if not map_path.is_file():
31
+ raise MapPolicyError(
32
+ "Repository map missing: .devcouncil/repo_map.json"
33
+ )
34
+ from devcouncil.utils.json_persist import read_json
35
+
36
+ loaded = read_json(map_path)
37
+ if not isinstance(loaded, dict):
38
+ raise MapPolicyError("Repository map is unreadable")
39
+
40
+
19
41
  def _log_gate(name: str, gaps: List[Gap], *, routine: bool = False, **context: Any) -> bool:
20
42
  """Log a gate decision and return whether it passed (no blocking gaps).
21
43
 
@@ -36,6 +58,13 @@ def _log_gate(name: str, gaps: List[Gap], *, routine: bool = False, **context: A
36
58
  name, suffix,
37
59
  "; ".join(f"{g.gap_type}: {g.description}" for g in blocking),
38
60
  )
61
+ log_step(
62
+ f"gate/{name}: {'PASSED' if passed else 'FAILED'}",
63
+ level=logging.INFO if passed else logging.WARNING,
64
+ blocking=len(blocking),
65
+ advisory=len(gaps) - len(blocking),
66
+ **context,
67
+ )
39
68
  return passed
40
69
 
41
70
 
@@ -108,6 +137,7 @@ class GatePolicy:
108
137
  self.req_coverage = RequirementCoverageCheck()
109
138
  self.planned_files = PlannedFilesCheck()
110
139
  self.clean_git = CleanGitCheck()
140
+ self.map_policy = FailClosedMapPolicy()
111
141
 
112
142
  def check_plan_approval(
113
143
  self,
@@ -307,7 +337,21 @@ class GatePolicy:
307
337
  # 2. Check planned files
308
338
  gaps.extend(self.planned_files.check(task))
309
339
 
310
- # 3. Surface a missing execution/verification contract but do NOT block
340
+ # 3. Fail-closed map policy block when the repo map is missing/unreadable.
341
+ try:
342
+ self.map_policy.validate_repo_map(project_root)
343
+ except MapPolicyError as exc:
344
+ gaps.append(Gap(
345
+ id=f"GAP-{task.id}-MAP-POLICY",
346
+ severity="high",
347
+ gap_type="requirement_not_planned",
348
+ task_id=task.id,
349
+ description=f"Map policy blocked task readiness: {exc}",
350
+ recommended_fix="Run `dev map` to generate a valid repository map, then retry.",
351
+ blocking=True,
352
+ ))
353
+
354
+ # 4. Surface a missing execution/verification contract — but do NOT block
311
355
  # execution on it. The executor still needs to run to implement the code,
312
356
  # and the evidence requirement is genuinely enforced at verify time
313
357
  # (acceptance_criteria_unproven / NOAC gaps). Blocking here only prevents
@@ -366,4 +410,4 @@ class GatePolicy:
366
410
  recommended_fix="Break the cycle so the tasks can be ordered and executed.",
367
411
  blocking=True,
368
412
  ))
369
- return gaps
413
+ return gaps
@@ -29,7 +29,7 @@ class AstMatch:
29
29
 
30
30
 
31
31
  class AstMatcher:
32
- """Tree-sitter-style structural search with optional tree_sitter and deterministic fallbacks."""
32
+ """Structural symbol search with tree-sitter when available, regex/AST fallbacks."""
33
33
 
34
34
  _EXT_LANGUAGE = {
35
35
  ".py": "python",
@@ -59,10 +59,11 @@ class AstMatcher:
59
59
 
60
60
  def _has_tree_sitter(self) -> bool:
61
61
  try:
62
- import tree_sitter # type: ignore[import-not-found] # noqa: F401
62
+ from devcouncil.indexing.ts_imports import tree_sitter_available
63
+
64
+ return tree_sitter_available()
63
65
  except Exception:
64
66
  return False
65
- return True
66
67
 
67
68
  def match(
68
69
  self,
@@ -118,6 +119,38 @@ class AstMatcher:
118
119
  def _match_file(self, rel: str, language: str, text: str, *, query: str, kind: str | None) -> list[AstMatch]:
119
120
  if language == "python":
120
121
  return self._match_python(rel, text, query=query, kind=kind)
122
+ if self.tree_sitter_available and language in self._SYMBOL_PATTERNS:
123
+ ts_hits = self._match_tree_sitter(rel, language, text, query=query, kind=kind)
124
+ if ts_hits is not None:
125
+ return ts_hits
126
+ return self._match_regex(rel, language, text, query=query, kind=kind)
127
+
128
+ def _match_tree_sitter(
129
+ self, rel: str, language: str, text: str, *, query: str, kind: str | None
130
+ ) -> list[AstMatch] | None:
131
+ try:
132
+ from devcouncil.indexing.ts_imports import extract_symbols
133
+
134
+ hits = extract_symbols(language, text)
135
+ except Exception:
136
+ return None
137
+ if hits is None:
138
+ return None
139
+ results: list[AstMatch] = []
140
+ q = query.lower() if query else ""
141
+ for symbol_kind, symbol_name, lineno, source in hits:
142
+ if kind and kind != symbol_kind:
143
+ continue
144
+ if q and q not in symbol_name.lower() and q not in source.lower():
145
+ continue
146
+ results.append(
147
+ AstMatch(rel, language, symbol_kind, symbol_name, lineno, source, "tree-sitter")
148
+ )
149
+ return sorted(results, key=lambda item: (item.path, item.line))
150
+
151
+ def _match_regex(
152
+ self, rel: str, language: str, text: str, *, query: str, kind: str | None
153
+ ) -> list[AstMatch]:
121
154
  pattern = self._SYMBOL_PATTERNS.get(language)
122
155
  if not pattern:
123
156
  return []
@@ -132,7 +165,9 @@ class AstMatcher:
132
165
  continue
133
166
  if query and query.lower() not in symbol_name.lower() and query.lower() not in line.lower():
134
167
  continue
135
- results.append(AstMatch(rel, language, symbol_kind, symbol_name, lineno, line.strip(), self._engine()))
168
+ results.append(
169
+ AstMatch(rel, language, symbol_kind, symbol_name, lineno, line.strip(), self._engine())
170
+ )
136
171
  return results
137
172
 
138
173
  def _match_python(self, rel: str, text: str, *, query: str, kind: str | None) -> list[AstMatch]:
@@ -175,4 +210,6 @@ class AstMatcher:
175
210
  return "function"
176
211
 
177
212
  def _engine(self) -> str:
213
+ # Python always uses the stdlib AST; other languages report tree-sitter when
214
+ # the optional extra is installed even if a given file fell back to regex.
178
215
  return "tree-sitter-optional" if self.tree_sitter_available else "fallback-ast"
@@ -0,0 +1,78 @@
1
+ """Symbol-level code knowledge graph for DevCouncil repo mapping.
2
+
3
+ Public entry points: :func:`build_code_graph`, :func:`refresh_map_for_paths`,
4
+ :func:`load_code_graph`, :func:`query_symbol`, :func:`trace_path`, plus
5
+ graph intelligence (:func:`enrich_graph_intel`, :func:`diff_impact`, …).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from devcouncil.indexing.graph.build import (
11
+ build_code_graph,
12
+ load_code_graph,
13
+ refresh_map_for_paths,
14
+ write_code_graph,
15
+ )
16
+ from devcouncil.indexing.graph.export import (
17
+ build_code_graph_okf,
18
+ export_graphml,
19
+ file_doc_rel,
20
+ write_code_graph_okf,
21
+ )
22
+ from devcouncil.indexing.graph.intel import (
23
+ blast_radius,
24
+ circular_imports,
25
+ compute_communities,
26
+ diff_impact,
27
+ enrich_graph_intel,
28
+ extract_processes,
29
+ god_nodes,
30
+ graph_check,
31
+ )
32
+ from devcouncil.indexing.graph.query import query_symbol, trace_path
33
+ from devcouncil.indexing.graph.schema import (
34
+ SCHEMA_VERSION,
35
+ CodeGraph,
36
+ Confidence,
37
+ DeadCodeEntry,
38
+ GraphEdge,
39
+ GraphNode,
40
+ NodeKind,
41
+ )
42
+
43
+ __all__ = [
44
+ "SCHEMA_VERSION",
45
+ "CodeGraph",
46
+ "Confidence",
47
+ "DeadCodeEntry",
48
+ "GraphEdge",
49
+ "GraphNode",
50
+ "NodeKind",
51
+ "blast_radius",
52
+ "build_code_graph",
53
+ "build_code_graph_okf",
54
+ "circular_imports",
55
+ "compute_communities",
56
+ "diff_impact",
57
+ "enrich_graph_intel",
58
+ "export_graphml",
59
+ "extract_processes",
60
+ "file_doc_rel",
61
+ "god_nodes",
62
+ "graph_check",
63
+ "load_code_graph",
64
+ "query_symbol",
65
+ "refresh_map_for_paths",
66
+ "trace_path",
67
+ "write_code_graph",
68
+ "write_code_graph_okf",
69
+ ]
70
+
71
+
72
+ def __getattr__(name: str):
73
+ """Lazy OKF facade symbols (avoid import cycles with repo_mapper)."""
74
+ if name in {"build_graph_okf_bundle", "export_graph_okf", "graph_to_graphml"}:
75
+ from devcouncil.indexing.graph import okf_export
76
+
77
+ return getattr(okf_export, name)
78
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")