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
@@ -0,0 +1,65 @@
1
+ """Compatibility facade for code-graph OKF export.
2
+
3
+ Prefers the shared implementation in :mod:`devcouncil.indexing.graph.export`
4
+ and re-exports helpers used by wiki link conventions.
5
+
6
+ devcouncil: allow-unwired — thin re-export surface; callers use graph.export directly.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from pathlib import Path
12
+ from typing import List
13
+
14
+ from devcouncil.indexing.graph.export import (
15
+ build_code_graph_okf,
16
+ export_graphml,
17
+ file_doc_rel,
18
+ write_code_graph_okf,
19
+ )
20
+ from devcouncil.indexing.graph.export_links import (
21
+ GRAPH_FROM_WIKI,
22
+ WIKI_FROM_GRAPH,
23
+ cross_bundle_file_link,
24
+ file_doc_path,
25
+ relative_md_link,
26
+ slugify_area,
27
+ subsystem_doc_path,
28
+ wired_to_bullets,
29
+ )
30
+ from devcouncil.indexing.graph.schema import CodeGraph
31
+
32
+ # Public aliases matching the Phase-7 plan names
33
+ build_graph_okf_bundle = build_code_graph_okf
34
+ graph_to_graphml = export_graphml
35
+
36
+
37
+ def export_graph_okf(
38
+ graph: CodeGraph,
39
+ out_dir: Path,
40
+ *,
41
+ root: Path | None = None,
42
+ project_name: str = "Code Graph",
43
+ ) -> List[Path]:
44
+ """Write an OKF v0.1 bundle for ``graph`` under ``out_dir``."""
45
+ base = root or Path(".")
46
+ _out, written = write_code_graph_okf(
47
+ base, out_dir, graph=graph, project_name=project_name
48
+ )
49
+ return written
50
+
51
+
52
+ __all__ = [
53
+ "GRAPH_FROM_WIKI",
54
+ "WIKI_FROM_GRAPH",
55
+ "build_graph_okf_bundle",
56
+ "cross_bundle_file_link",
57
+ "export_graph_okf",
58
+ "file_doc_path",
59
+ "file_doc_rel",
60
+ "graph_to_graphml",
61
+ "relative_md_link",
62
+ "slugify_area",
63
+ "subsystem_doc_path",
64
+ "wired_to_bullets",
65
+ ]
@@ -0,0 +1,67 @@
1
+ """Program dependence graph (PDG) layer — CFG, reaching-def, CDG, taint."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from devcouncil.indexing.graph.pdg.build import (
6
+ build_pdg_for_paths,
7
+ load_pdg_layer,
8
+ merge_pdg_into_graph,
9
+ )
10
+ from devcouncil.indexing.graph.pdg.cdg import build_cdg
11
+ from devcouncil.indexing.graph.pdg.cfg import CFGResult, block_for_line, build_cfg_for_function
12
+ from devcouncil.indexing.graph.pdg.reaching_def import (
13
+ BlockDefUse,
14
+ block_def_use,
15
+ compute_reaching_defs,
16
+ )
17
+ from devcouncil.indexing.graph.pdg.schema import (
18
+ PDG_VERSION,
19
+ BasicBlock,
20
+ CDGEdge,
21
+ CFGEdge,
22
+ DataDepEdge,
23
+ FilePDG,
24
+ FunctionPDG,
25
+ PDGLayer,
26
+ TaintCategory,
27
+ TaintFinding,
28
+ )
29
+ from devcouncil.indexing.graph.pdg.taint import analyze_taint
30
+ from devcouncil.indexing.graph.pdg.query import (
31
+ explain_pdg_taint,
32
+ explain_taint,
33
+ query_controls,
34
+ query_flows,
35
+ query_pdg_controls,
36
+ query_pdg_flows,
37
+ )
38
+
39
+ __all__ = [
40
+ "PDG_VERSION",
41
+ "BasicBlock",
42
+ "BlockDefUse",
43
+ "CDGEdge",
44
+ "CFGEdge",
45
+ "CFGResult",
46
+ "DataDepEdge",
47
+ "FilePDG",
48
+ "FunctionPDG",
49
+ "PDGLayer",
50
+ "TaintCategory",
51
+ "TaintFinding",
52
+ "analyze_taint",
53
+ "block_def_use",
54
+ "block_for_line",
55
+ "build_cdg",
56
+ "build_cfg_for_function",
57
+ "build_pdg_for_paths",
58
+ "compute_reaching_defs",
59
+ "explain_pdg_taint",
60
+ "explain_taint",
61
+ "load_pdg_layer",
62
+ "merge_pdg_into_graph",
63
+ "query_controls",
64
+ "query_flows",
65
+ "query_pdg_controls",
66
+ "query_pdg_flows",
67
+ ]
@@ -0,0 +1,11 @@
1
+ """Thin re-export of PDG build helpers (see graph.build)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from devcouncil.indexing.graph.build import (
6
+ build_pdg_for_paths,
7
+ load_pdg_layer,
8
+ merge_pdg_into_graph,
9
+ )
10
+
11
+ __all__ = ["build_pdg_for_paths", "load_pdg_layer", "merge_pdg_into_graph"]
@@ -0,0 +1,41 @@
1
+ """Control-dependence graph from CFG branch structure (MVP)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ from typing import List, Set
7
+
8
+ from devcouncil.indexing.graph.pdg.cfg import CFGResult
9
+ from devcouncil.indexing.graph.pdg.schema import BasicBlock, CDGEdge
10
+
11
+
12
+ def _is_guard_block(block: BasicBlock) -> bool:
13
+ text = block.text.strip()
14
+ return text.startswith("return") or text.startswith("raise")
15
+
16
+
17
+ def build_cdg(cfg: CFGResult, func_node: ast.AST) -> List[CDGEdge]:
18
+ """Build CDG edges from branch terminators to dependent blocks."""
19
+ del func_node # reserved for richer branch conditions
20
+ cdg: List[CDGEdge] = []
21
+ seen: Set[tuple[str, str, str]] = set()
22
+ branch_kinds = {"true", "false", "loop", "exception"}
23
+ for edge in cfg.edges:
24
+ if edge.kind not in branch_kinds:
25
+ continue
26
+ branch = "T" if edge.kind in {"true", "loop"} else "F"
27
+ key = (edge.source, edge.target, branch)
28
+ if key in seen:
29
+ continue
30
+ seen.add(key)
31
+ dependent = next((b for b in cfg.blocks if b.id == edge.target), None)
32
+ guard = bool(dependent and _is_guard_block(dependent))
33
+ cdg.append(
34
+ CDGEdge(
35
+ controller=edge.source,
36
+ dependent=edge.target,
37
+ branch=branch,
38
+ guard=guard,
39
+ )
40
+ )
41
+ return cdg
@@ -0,0 +1,199 @@
1
+ """Python AST control-flow graph builder (intra-procedural MVP)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ from dataclasses import dataclass, field
7
+ from typing import Dict, List, Optional, Set, Tuple
8
+
9
+ from devcouncil.indexing.graph.pdg.schema import BasicBlock, CFGEdge
10
+
11
+
12
+ @dataclass
13
+ class CFGResult:
14
+ blocks: List[BasicBlock] = field(default_factory=list)
15
+ edges: List[CFGEdge] = field(default_factory=list)
16
+ entry: str = ""
17
+ exits: List[str] = field(default_factory=list)
18
+ block_by_line: Dict[int, str] = field(default_factory=dict)
19
+
20
+
21
+ def _block_id(path: str, qualname: str, start_line: int, index: int) -> str:
22
+ return f"{path}::{qualname}:{start_line}:bb{index}"
23
+
24
+
25
+ def _stmt_lines(stmts: List[ast.stmt]) -> Tuple[int, int]:
26
+ lines: List[int] = []
27
+ for stmt in stmts:
28
+ if hasattr(stmt, "lineno"):
29
+ lines.append(int(stmt.lineno))
30
+ if hasattr(stmt, "end_lineno") and stmt.end_lineno:
31
+ lines.append(int(stmt.end_lineno))
32
+ if not lines:
33
+ return 0, 0
34
+ return min(lines), max(lines)
35
+
36
+
37
+ def _first_line(node: ast.AST, default: int = 0) -> int:
38
+ return int(getattr(node, "lineno", default) or default)
39
+
40
+
41
+ def _is_leader(stmt: ast.stmt) -> bool:
42
+ return isinstance(
43
+ stmt,
44
+ (ast.If, ast.While, ast.For, ast.AsyncFor, ast.Try, ast.Return, ast.Raise, ast.Break, ast.Continue),
45
+ )
46
+
47
+
48
+ class _CFGBuilder:
49
+ def __init__(self, path: str, qualname: str, source_lines: List[str]) -> None:
50
+ self.path = path
51
+ self.qualname = qualname
52
+ self.source_lines = source_lines
53
+ self.blocks: List[BasicBlock] = []
54
+ self.edges: List[CFGEdge] = []
55
+ self._index = 0
56
+ self.block_by_line: Dict[int, str] = {}
57
+ self.exits: Set[str] = set()
58
+
59
+ def _new_block(self, stmts: List[ast.stmt]) -> str:
60
+ start, end = _stmt_lines(stmts)
61
+ if not start and self.blocks:
62
+ start = self.blocks[-1].end_line or self.blocks[-1].start_line
63
+ end = start
64
+ bid = _block_id(self.path, self.qualname, start or 1, self._index)
65
+ self._index += 1
66
+ text_lines: List[str] = []
67
+ line_nums: List[int] = []
68
+ for stmt in stmts:
69
+ ln = _first_line(stmt)
70
+ if ln:
71
+ line_nums.append(ln)
72
+ if 0 < ln <= len(self.source_lines):
73
+ text_lines.append(self.source_lines[ln - 1].rstrip())
74
+ block = BasicBlock(
75
+ id=bid,
76
+ start_line=start or 1,
77
+ end_line=end or start or 1,
78
+ text="\n".join(text_lines),
79
+ lines=line_nums,
80
+ )
81
+ self.blocks.append(block)
82
+ for ln in line_nums:
83
+ self.block_by_line.setdefault(ln, bid)
84
+ return bid
85
+
86
+ def _connect(self, source: Optional[str], target: Optional[str], kind: str = "fallthrough") -> None:
87
+ if source and target and source != target:
88
+ self.edges.append(CFGEdge(source=source, target=target, kind=kind))
89
+
90
+ def _build_stmts(self, stmts: List[ast.stmt], *, after: Optional[str] = None) -> Tuple[Optional[str], List[str]]:
91
+ if not stmts:
92
+ return after, []
93
+ current_after = after
94
+ pending_exits: List[str] = []
95
+ i = 0
96
+ while i < len(stmts):
97
+ group: List[ast.stmt] = []
98
+ while i < len(stmts) and not _is_leader(stmts[i]):
99
+ group.append(stmts[i])
100
+ i += 1
101
+ block_id: Optional[str] = None
102
+ if group:
103
+ block_id = self._new_block(group)
104
+ self._connect(current_after, block_id)
105
+ current_after = block_id
106
+ if i >= len(stmts):
107
+ break
108
+ stmt = stmts[i]
109
+ i += 1
110
+ if isinstance(stmt, (ast.Return, ast.Raise)):
111
+ term = block_id or current_after or self._new_block([stmt])
112
+ ln = _first_line(stmt)
113
+ if ln:
114
+ self.block_by_line[ln] = term
115
+ self.exits.add(term)
116
+ current_after = None
117
+ elif isinstance(stmt, ast.If):
118
+ test_block = block_id or current_after or self._new_block([stmt])
119
+ then_end, then_exits = self._build_stmts(stmt.body)
120
+ else_end, else_exits = self._build_stmts(stmt.orelse) if stmt.orelse else (test_block, [])
121
+ merge = self._new_block([])
122
+ if then_end:
123
+ self._connect(then_end, merge)
124
+ self._connect(test_block, then_end, "true")
125
+ else:
126
+ self._connect(test_block, merge, "true")
127
+ if stmt.orelse:
128
+ if else_end:
129
+ self._connect(else_end, merge)
130
+ self._connect(test_block, else_end, "false")
131
+ else:
132
+ self._connect(test_block, merge, "false")
133
+ else:
134
+ self._connect(test_block, merge, "false")
135
+ pending_exits.extend(then_exits + else_exits)
136
+ current_after = merge
137
+ elif isinstance(stmt, (ast.While, ast.For, ast.AsyncFor)):
138
+ loop_header = block_id or current_after or self._new_block([stmt])
139
+ body_end, body_exits = self._build_stmts(stmt.body)
140
+ if body_end:
141
+ self._connect(body_end, loop_header, "loop")
142
+ after_loop = self._new_block([])
143
+ self._connect(loop_header, after_loop, "false")
144
+ pending_exits.extend(body_exits)
145
+ current_after = after_loop
146
+ elif isinstance(stmt, ast.Try):
147
+ _ = block_id or current_after or self._new_block([stmt])
148
+ try_end, try_exits = self._build_stmts(stmt.body)
149
+ join = self._new_block([])
150
+ if try_end:
151
+ self._connect(try_end, join)
152
+ for handler in stmt.handlers:
153
+ h_end, h_exits = self._build_stmts(handler.body)
154
+ if h_end:
155
+ self._connect(h_end, join, "exception")
156
+ pending_exits.extend(h_exits)
157
+ pending_exits.extend(try_exits)
158
+ current_after = join
159
+ else:
160
+ group2 = [stmt]
161
+ while i < len(stmts) and not _is_leader(stmts[i]):
162
+ group2.append(stmts[i])
163
+ i += 1
164
+ bid2 = self._new_block(group2)
165
+ self._connect(current_after, bid2)
166
+ current_after = bid2
167
+ return current_after, pending_exits
168
+
169
+ def build(self, body: List[ast.stmt]) -> CFGResult:
170
+ entry = self._new_block([])
171
+ end, exits = self._build_stmts(body, after=entry)
172
+ if end:
173
+ self.exits.add(end)
174
+ self.exits.update(exits)
175
+ return CFGResult(
176
+ blocks=self.blocks,
177
+ edges=self.edges,
178
+ entry=entry,
179
+ exits=sorted(self.exits),
180
+ block_by_line=self.block_by_line,
181
+ )
182
+
183
+
184
+ def build_cfg_for_function(
185
+ path: str,
186
+ qualname: str,
187
+ node: ast.AST,
188
+ source_lines: List[str],
189
+ ) -> CFGResult:
190
+ body: List[ast.stmt] = []
191
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
192
+ body = list(node.body)
193
+ elif isinstance(node, ast.Module):
194
+ body = list(node.body)
195
+ return _CFGBuilder(path, qualname, source_lines).build(body)
196
+
197
+
198
+ def block_for_line(cfg: CFGResult, line: int) -> Optional[str]:
199
+ return cfg.block_by_line.get(line)
@@ -0,0 +1,21 @@
1
+ """Thin re-export of PDG query helpers (see graph.query)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from devcouncil.indexing.graph.query import (
6
+ explain_pdg_taint,
7
+ explain_taint,
8
+ query_controls,
9
+ query_flows,
10
+ query_pdg_controls,
11
+ query_pdg_flows,
12
+ )
13
+
14
+ __all__ = [
15
+ "explain_pdg_taint",
16
+ "explain_taint",
17
+ "query_controls",
18
+ "query_flows",
19
+ "query_pdg_controls",
20
+ "query_pdg_flows",
21
+ ]
@@ -0,0 +1,126 @@
1
+ """Reaching-definitions analysis on a CFG."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ from dataclasses import dataclass, field
7
+ from typing import Dict, Iterable, List, Set, Tuple
8
+
9
+ from devcouncil.indexing.graph.pdg.cfg import CFGResult
10
+ from devcouncil.indexing.graph.pdg.schema import DataDepEdge
11
+
12
+
13
+ @dataclass
14
+ class BlockDefUse:
15
+ gen: Set[Tuple[str, int]] = field(default_factory=set)
16
+ kill: Set[str] = field(default_factory=set)
17
+ use: Set[Tuple[str, int]] = field(default_factory=set)
18
+
19
+
20
+ def _store_target(node: ast.AST) -> str | None:
21
+ if isinstance(node, ast.Name):
22
+ return node.id
23
+ if isinstance(node, ast.Attribute):
24
+ return node.attr
25
+ return None
26
+
27
+
28
+ class _DefUseVisitor(ast.NodeVisitor):
29
+ def __init__(self, block_lines: Set[int]) -> None:
30
+ self.block_lines = block_lines
31
+ self.gen: Set[Tuple[str, int]] = set()
32
+ self.kill: Set[str] = set()
33
+ self.use: Set[Tuple[str, int]] = set()
34
+
35
+ def _in_block(self, node: ast.AST) -> bool:
36
+ ln = getattr(node, "lineno", None)
37
+ return ln is not None and int(ln) in self.block_lines
38
+
39
+ def visit_Name(self, node: ast.Name) -> None:
40
+ if not self._in_block(node):
41
+ return
42
+ if isinstance(node.ctx, ast.Load):
43
+ self.use.add((node.id, int(node.lineno)))
44
+ elif isinstance(node.ctx, ast.Store):
45
+ self.gen.add((node.id, int(node.lineno)))
46
+ self.kill.add(node.id)
47
+
48
+ def visit_Attribute(self, node: ast.Attribute) -> None:
49
+ if isinstance(node.ctx, ast.Load) and self._in_block(node):
50
+ self.use.add((node.attr, int(node.lineno)))
51
+ self.generic_visit(node)
52
+
53
+ def visit_arg(self, node: ast.arg) -> None:
54
+ if node.lineno in self.block_lines:
55
+ self.gen.add((node.arg, int(node.lineno)))
56
+
57
+
58
+ def block_def_use(node: ast.AST, block_lines: Iterable[int]) -> BlockDefUse:
59
+ lines = set(block_lines)
60
+ visitor = _DefUseVisitor(lines)
61
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
62
+ for arg in node.args.args + node.args.kwonlyargs:
63
+ if arg.lineno in lines:
64
+ visitor.gen.add((arg.arg, int(arg.lineno)))
65
+ for d in node.args.defaults + node.args.kw_defaults:
66
+ if d is not None:
67
+ visitor.visit(d)
68
+ visitor.visit(node)
69
+ else:
70
+ visitor.visit(node)
71
+ return BlockDefUse(gen=visitor.gen, kill=visitor.kill, use=visitor.use)
72
+
73
+
74
+ def compute_reaching_defs(
75
+ cfg: CFGResult,
76
+ func_node: ast.AST,
77
+ ) -> List[DataDepEdge]:
78
+ """Iterative reaching-definitions; emit def->use edges."""
79
+ block_map = {b.id: set(b.lines) for b in cfg.blocks}
80
+ in_sets: Dict[str, Set[Tuple[str, int]]] = {b.id: set() for b in cfg.blocks}
81
+ out_sets: Dict[str, Set[Tuple[str, int]]] = {b.id: set() for b in cfg.blocks}
82
+ du: Dict[str, BlockDefUse] = {}
83
+ for block in cfg.blocks:
84
+ du[block.id] = block_def_use(func_node, block_map[block.id])
85
+
86
+ preds: Dict[str, Set[str]] = {b.id: set() for b in cfg.blocks}
87
+ for edge in cfg.edges:
88
+ preds.setdefault(edge.target, set()).add(edge.source)
89
+
90
+ changed = True
91
+ while changed:
92
+ changed = False
93
+ for block in cfg.blocks:
94
+ bid = block.id
95
+ new_in: Set[Tuple[str, int]] = set()
96
+ for pred in preds.get(bid, set()):
97
+ new_in |= out_sets.get(pred, set())
98
+ if new_in != in_sets[bid]:
99
+ in_sets[bid] = new_in
100
+ changed = True
101
+ gen = du[bid].gen
102
+ kill = du[bid].kill
103
+ new_out = (new_in - {(v, ln) for v, ln in new_in if v in kill}) | gen
104
+ if new_out != out_sets[bid]:
105
+ out_sets[bid] = new_out
106
+ changed = True
107
+
108
+ edges: List[DataDepEdge] = []
109
+ for block in cfg.blocks:
110
+ bid = block.id
111
+ reaching = in_sets[bid] | du[bid].gen
112
+ for var, use_line in du[bid].use:
113
+ for def_var, def_line in reaching:
114
+ if def_var == var and def_line <= use_line:
115
+ def_block = cfg.block_by_line.get(def_line, bid)
116
+ use_block = cfg.block_by_line.get(use_line, bid)
117
+ edges.append(
118
+ DataDepEdge(
119
+ variable=var,
120
+ def_line=def_line,
121
+ use_line=use_line,
122
+ def_block=def_block,
123
+ use_block=use_block,
124
+ )
125
+ )
126
+ return edges