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,3 +1,7 @@
1
+ from devcouncil.artifacts.coverage import (
2
+ acceptance_criteria_evidence_matrix,
3
+ requirement_task_matrix,
4
+ )
1
5
  from devcouncil.artifacts.graph import ArtifactGraph
2
6
 
3
7
  class MarkdownReportGenerator:
@@ -6,7 +10,11 @@ class MarkdownReportGenerator:
6
10
  MAX_INLINE_GAPS = 25
7
11
 
8
12
  @staticmethod
9
- def generate(graph: ArtifactGraph, live_review: dict | None = None) -> str:
13
+ def generate(
14
+ graph: ArtifactGraph,
15
+ live_review: dict | None = None,
16
+ wiki_refresh: dict | None = None,
17
+ ) -> str:
10
18
  summary = graph.coverage_summary()
11
19
  live_blockers = (live_review or {}).get("blocking_cards", [])
12
20
 
@@ -56,15 +64,27 @@ class MarkdownReportGenerator:
56
64
  md_output += "\n"
57
65
 
58
66
  md_output += "## Requirements Coverage Table\n"
59
- md_output += "| Requirement | Task Mapping | Status |\n"
67
+ md_output += "| Requirement | Task | Task status |\n"
60
68
  md_output += "|---|---|---|\n"
61
-
62
- for req in graph.requirements.values():
63
- linked_tasks = [t for t in graph.tasks.values() if req.id in t.requirement_ids]
64
- task_str = ", ".join([t.id for t in linked_tasks]) if linked_tasks else "*None*"
65
- status_str = "Covered" if linked_tasks else "**Unmapped**"
66
- md_output += f"| {req.id} {req.title} | {task_str} | {status_str} |\n"
67
-
69
+ for row in requirement_task_matrix(graph):
70
+ title = row.get("title") or ""
71
+ task = row.get("task") or "(none)"
72
+ status = row.get("task_status") or ""
73
+ if task == "(none)":
74
+ task = "*None*"
75
+ status = "**Unmapped**"
76
+ md_output += f"| {row['requirement']} {title} | {task} | {status} |\n"
77
+
78
+ ac_rows = acceptance_criteria_evidence_matrix(graph)
79
+ if ac_rows:
80
+ md_output += "\n## Acceptance Criteria Evidence\n"
81
+ md_output += "| Requirement | AC | Status |\n"
82
+ md_output += "|---|---|---|\n"
83
+ for row in ac_rows:
84
+ md_output += (
85
+ f"| {row['requirement_id']} | {row['ac_id']} | {row['status']} |\n"
86
+ )
87
+
68
88
  md_output += "\n## Blocking Gaps\n"
69
89
  blocking_gaps = graph.blocking_gaps()
70
90
  if not blocking_gaps:
@@ -92,5 +112,14 @@ class MarkdownReportGenerator:
92
112
  if card.get("task_id"):
93
113
  md_output += f" (`{card['task_id']}`)"
94
114
  md_output += f": {card['summary']}\n"
115
+
116
+ if wiki_refresh is not None and wiki_refresh.get("considered"):
117
+ md_output += "\n## Wiki Refresh\n"
118
+ md_output += f"- **Reason**: {wiki_refresh.get('reason', '')}\n"
119
+ stale = wiki_refresh.get("stale_pages") or []
120
+ if stale:
121
+ md_output += f"- **Stale pages**: {len(stale)}\n"
122
+ for page in stale[:10]:
123
+ md_output += f" - {page}\n"
95
124
 
96
125
  return md_output
@@ -0,0 +1,142 @@
1
+ """MCP corpus resource read/list helpers shared by CLI and MCP server."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from devcouncil.cli.commands.init import initialize_project
8
+ from devcouncil.knowledge.resource_discovery import (
9
+ discover_knowledge_sources,
10
+ knowledge_source_uri,
11
+ )
12
+ from devcouncil.live.summary import live_review_summary
13
+ from devcouncil.reporting.report_builder import ReportBuilder
14
+ from devcouncil.storage.db import get_db
15
+ from devcouncil.storage.repositories import ArtifactGraphRepository, GapRepository, TaskRepository
16
+ from devcouncil.utils.json_persist import dump_json
17
+
18
+
19
+ def read_mcp_resource(project_root: Path, uri: str) -> str:
20
+ """Return the body for one ``devcouncil://`` resource URI."""
21
+ initialize_project(project_root, quiet=True)
22
+ db = get_db(project_root)
23
+ key = uri.rstrip("/")
24
+
25
+ if key == "devcouncil://report":
26
+ if not db:
27
+ return "DevCouncil is not initialized in this directory."
28
+ with db.get_session() as session:
29
+ graph = ArtifactGraphRepository(session).load_graph()
30
+ return ReportBuilder.build_markdown(graph, live_review=live_review_summary(project_root))
31
+
32
+ if key == "devcouncil://tasks":
33
+ if not db:
34
+ return dump_json({"tasks": []}, indent=2)
35
+ with db.get_session() as session:
36
+ tasks = [t.model_dump() for t in TaskRepository(session).get_all()]
37
+ return dump_json({"tasks": tasks}, indent=2)
38
+
39
+ if key == "devcouncil://gaps":
40
+ if not db:
41
+ return dump_json({"gaps": []}, indent=2)
42
+ with db.get_session() as session:
43
+ gaps = [g.model_dump() for g in GapRepository(session).get_all()]
44
+ return dump_json({"gaps": gaps}, indent=2)
45
+
46
+ if key == "devcouncil://cards":
47
+ return dump_json(live_review_summary(project_root), indent=2)
48
+
49
+ if key.startswith("devcouncil://task/"):
50
+ task_id = key.rsplit("/", 1)[-1]
51
+ if not db:
52
+ return dump_json({"ok": False, "error": "not initialized"}, indent=2)
53
+ with db.get_session() as session:
54
+ task = TaskRepository(session).get_by_id(task_id)
55
+ if not task:
56
+ return dump_json({"ok": False, "error": f"Task {task_id} not found."}, indent=2)
57
+ gaps = [g.model_dump() for g in GapRepository(session).get_for_task(task_id)]
58
+ return dump_json({"task": task.model_dump(), "gaps": gaps}, indent=2)
59
+
60
+ if key == "devcouncil://knowledge":
61
+ sources = discover_knowledge_sources(project_root)
62
+ if not sources:
63
+ return "# Project knowledge\n\nNo OKF or design knowledge has been ingested for this project."
64
+ lines = ["# Project knowledge", "", "Ingested OKF and design knowledge for this project.", ""]
65
+ for kind in ("design", "okf"):
66
+ kind_sources = [s for s in sources if s.kind == kind]
67
+ if not kind_sources:
68
+ continue
69
+ lines.append(f"## {kind.upper() if kind == 'okf' else kind.capitalize()}")
70
+ lines.append("")
71
+ for source in kind_sources:
72
+ link = knowledge_source_uri(source.kind, source.name)
73
+ desc = source.description or source.name
74
+ lines.append(f"- [{desc}]({link})")
75
+ lines.append("")
76
+ return "\n".join(lines).strip()
77
+
78
+ if key.startswith("devcouncil://knowledge/"):
79
+ for source in discover_knowledge_sources(project_root):
80
+ if knowledge_source_uri(source.kind, source.name) == key:
81
+ body = source.render() or source.body
82
+ return str(body) if body is not None else ""
83
+ return f"Knowledge source not found: {key}"
84
+
85
+ raise ValueError(f"Unknown resource: {uri}")
86
+
87
+
88
+ def list_mcp_resource_uris(project_root: Path) -> list[dict[str, str]]:
89
+ """Stable resource descriptors for MCP ``list_resources`` (without pydantic types)."""
90
+ initialize_project(project_root, quiet=True)
91
+ resources: list[dict[str, str]] = [
92
+ {
93
+ "uri": "devcouncil://report",
94
+ "name": "DevCouncil report",
95
+ "description": "Coverage report, requirement/task mapping, and blocking gaps.",
96
+ "mimeType": "text/markdown",
97
+ },
98
+ {
99
+ "uri": "devcouncil://tasks",
100
+ "name": "Tasks",
101
+ "description": "All planned tasks with scope and status.",
102
+ "mimeType": "application/json",
103
+ },
104
+ {
105
+ "uri": "devcouncil://gaps",
106
+ "name": "Gaps",
107
+ "description": "All open verification gaps.",
108
+ "mimeType": "application/json",
109
+ },
110
+ {
111
+ "uri": "devcouncil://cards",
112
+ "name": "Live review",
113
+ "description": "Live-review summary: cards, signals, and blockers.",
114
+ "mimeType": "application/json",
115
+ },
116
+ ]
117
+ db = get_db(project_root)
118
+ if db:
119
+ with db.get_session() as session:
120
+ for task in TaskRepository(session).get_all():
121
+ resources.append({
122
+ "uri": f"devcouncil://task/{task.id}",
123
+ "name": f"Task {task.id}: {task.title}",
124
+ "description": f"Scope, status, and gaps for {task.id}.",
125
+ "mimeType": "application/json",
126
+ })
127
+ knowledge_sources = discover_knowledge_sources(project_root)
128
+ if knowledge_sources:
129
+ resources.append({
130
+ "uri": "devcouncil://knowledge",
131
+ "name": "Project knowledge",
132
+ "description": "Index of ingested OKF and design knowledge for this project.",
133
+ "mimeType": "text/markdown",
134
+ })
135
+ for source in knowledge_sources:
136
+ resources.append({
137
+ "uri": knowledge_source_uri(source.kind, source.name),
138
+ "name": f"Knowledge ({source.kind}): {source.description or source.name}",
139
+ "description": source.description or source.name,
140
+ "mimeType": "text/markdown",
141
+ })
142
+ return resources
@@ -3,17 +3,53 @@ from pathlib import Path
3
3
  from devcouncil.artifacts.graph import ArtifactGraph
4
4
  from devcouncil.reporting.markdown_report import MarkdownReportGenerator
5
5
  from devcouncil.reporting.json_report import JsonReportGenerator
6
+ from devcouncil.reporting.evidence_export import EvidenceExportGenerator
7
+ from devcouncil.reporting.evidence_html import EvidenceHtmlGenerator
6
8
 
7
9
  class ReportBuilder:
8
10
  """Builds reports in various formats from the artifact graph."""
9
11
 
10
12
  @staticmethod
11
- def build_markdown(graph: ArtifactGraph, live_review: dict | None = None) -> str:
12
- return MarkdownReportGenerator.generate(graph, live_review=live_review)
13
+ def build_markdown(
14
+ graph: ArtifactGraph,
15
+ live_review: dict | None = None,
16
+ wiki_refresh: dict | None = None,
17
+ ) -> str:
18
+ return MarkdownReportGenerator.generate(
19
+ graph, live_review=live_review, wiki_refresh=wiki_refresh
20
+ )
21
+
22
+ @staticmethod
23
+ def build_json(
24
+ graph: ArtifactGraph,
25
+ live_review: dict | None = None,
26
+ wiki_refresh: dict | None = None,
27
+ ) -> str:
28
+ return JsonReportGenerator.generate(
29
+ graph, live_review=live_review, wiki_refresh=wiki_refresh
30
+ )
13
31
 
14
32
  @staticmethod
15
- def build_json(graph: ArtifactGraph, live_review: dict | None = None) -> str:
16
- return JsonReportGenerator.generate(graph, live_review=live_review)
33
+ def build_evidence_export(
34
+ graph: ArtifactGraph,
35
+ live_review: dict | None = None,
36
+ wiki_refresh: dict | None = None,
37
+ ) -> str:
38
+ """Reviewer-readable requirement→task→diff→evidence JSON (see evidence_export)."""
39
+ return EvidenceExportGenerator.generate(
40
+ graph, live_review=live_review, wiki_refresh=wiki_refresh
41
+ )
42
+
43
+ @staticmethod
44
+ def build_evidence_html(
45
+ graph: ArtifactGraph,
46
+ live_review: dict | None = None,
47
+ wiki_refresh: dict | None = None,
48
+ ) -> str:
49
+ """Self-contained HTML evidence report for CI artifact preview (see evidence_html)."""
50
+ return EvidenceHtmlGenerator.generate(
51
+ graph, live_review=live_review, wiki_refresh=wiki_refresh
52
+ )
17
53
 
18
54
  @staticmethod
19
55
  def build_okf_bundle(
@@ -0,0 +1,42 @@
1
+ """Task audit-trail payload shared by CLI and MCP."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from devcouncil.cli.commands.init import initialize_project
8
+ from devcouncil.domain.evidence import DiffCoverageEvidence
9
+ from devcouncil.storage.db import get_db
10
+ from devcouncil.storage.native import (
11
+ CorrectionManifestRepository,
12
+ FileChangeRepository,
13
+ VerificationRunRepository,
14
+ )
15
+ from devcouncil.storage.repositories import EvidenceRepository
16
+
17
+
18
+ def task_provenance_payload(project_root: Path, task_id: str) -> dict:
19
+ """Build the provenance audit trail for one task."""
20
+ initialize_project(project_root, quiet=True)
21
+ db = get_db(project_root)
22
+ if not db:
23
+ return {"ok": False, "error": "DevCouncil state is unavailable in this directory.", "task_id": task_id}
24
+
25
+ with db.get_session() as session:
26
+ file_changes = [r.model_dump() for r in FileChangeRepository(session).list_for_task(task_id)]
27
+ verification_runs = [r.model_dump() for r in VerificationRunRepository(session).list_for_task(task_id)]
28
+ coverage = [
29
+ ev.model_dump()
30
+ for ev in EvidenceRepository(session).get_all()
31
+ if isinstance(ev, DiffCoverageEvidence) and ev.task_id == task_id
32
+ ]
33
+ correction_manifest = CorrectionManifestRepository(session).latest_for_task(task_id)
34
+
35
+ return {
36
+ "ok": True,
37
+ "task_id": task_id,
38
+ "file_changes": file_changes,
39
+ "verification_runs": verification_runs,
40
+ "diff_coverage": coverage,
41
+ "latest_correction_manifest": correction_manifest.model_dump() if correction_manifest else None,
42
+ }
@@ -0,0 +1,75 @@
1
+ """Shared verdict classification for JSON and evidence exports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Literal
6
+
7
+ from devcouncil.artifacts.graph import ArtifactGraph
8
+
9
+ Verdict = Literal["passed", "blocked", "incomplete"]
10
+ IncompleteKind = Literal["quality", "infra"]
11
+
12
+ _INFRA_GAP_TYPES = frozenset({"invalid_verification_command"})
13
+ _INFRA_DESC_MARKERS = (
14
+ "sdk is not installed",
15
+ "not found on path",
16
+ "session limit",
17
+ "rate limit",
18
+ "too many requests",
19
+ "limit_rpm",
20
+ "unknown agent profile",
21
+ "failed to start or execute",
22
+ )
23
+
24
+
25
+ def _gaps_from_graph(graph: ArtifactGraph) -> list[Any]:
26
+ blocking = list(graph.blocking_gaps())
27
+ all_gaps = getattr(graph, "all_gaps", None)
28
+ if callable(all_gaps):
29
+ return list(all_gaps())
30
+ return blocking
31
+
32
+
33
+ def classify_incomplete_kind(
34
+ graph: ArtifactGraph,
35
+ *,
36
+ agent_run: dict[str, Any] | None = None,
37
+ ) -> IncompleteKind:
38
+ """Separate infra incomplete (executor/SDK/session) from quality incomplete."""
39
+ if agent_run:
40
+ returncode = agent_run.get("returncode")
41
+ if returncode not in (None, 0):
42
+ parts: list[str] = [str(agent_run.get("status") or "")]
43
+ for key in ("stderr_preview", "stdout_preview"):
44
+ val = agent_run.get(key)
45
+ if isinstance(val, list):
46
+ parts.extend(str(x) for x in val)
47
+ elif val:
48
+ parts.append(str(val))
49
+ hay = " ".join(parts).lower()
50
+ if any(marker in hay for marker in _INFRA_DESC_MARKERS):
51
+ return "infra"
52
+
53
+ for gap in _gaps_from_graph(graph):
54
+ gap_type = getattr(gap, "gap_type", "") or ""
55
+ if gap_type in _INFRA_GAP_TYPES:
56
+ return "infra"
57
+ desc = (getattr(gap, "description", "") or "").lower()
58
+ if any(marker in desc for marker in _INFRA_DESC_MARKERS):
59
+ return "infra"
60
+ return "quality"
61
+
62
+
63
+ def classify_verdict(
64
+ graph: ArtifactGraph,
65
+ *,
66
+ live_blockers: int = 0,
67
+ agent_run: dict[str, Any] | None = None,
68
+ ) -> tuple[Verdict, IncompleteKind | None]:
69
+ """Return ``(verdict, incomplete_kind)`` where kind is set only for incomplete."""
70
+ summary = graph.coverage_summary()
71
+ if summary["blocking_gaps"] > 0 or live_blockers > 0:
72
+ return "blocked", None
73
+ if summary["ac_without_evidence"] > 0:
74
+ return "incomplete", classify_incomplete_kind(graph, agent_run=agent_run)
75
+ return "passed", None
@@ -15,6 +15,7 @@ always: false # true = always selected (only core-engineering uses
15
15
  triggers: # how this skill is auto-selected (omit for always-on)
16
16
  keywords: [foo, bar] # matched against the goal text (case-insensitive substring)
17
17
  globs: ["*.foo", "build.bar"] # matched against repo file basenames
18
+ markers: [.devcouncil/config.yaml] # repo-relative paths that must exist (e.g. DevCouncil init)
18
19
  ---
19
20
 
20
21
  # Body
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: devcouncil-hero-loop
3
+ title: DevCouncil Hero Loop (Checkout → Verify → Release)
4
+ description: Run DevCouncil's autonomous task loop in Claude Code — checkout a lease, implement inside scope, verify with deterministic gates, self-repair from typed next_actions, then release.
5
+ triggers:
6
+ keywords: [hero loop, verify loop, checkout task, verify task, release task, next task, dev go, dev e2e, dev repair, task lease]
7
+ markers: [.devcouncil/config.yaml]
8
+ ---
9
+
10
+ # DevCouncil Hero Loop
11
+
12
+ DevCouncil's certified end-to-end path for Claude Code: the agent checks out a task,
13
+ implements inside declared scope, verifies deterministically, self-repairs from typed
14
+ next actions, and releases — **without a human pasting test output back and forth.**
15
+
16
+ ## The loop
17
+
18
+ ```
19
+ checkout_task ─▶ (agent implements) ─▶ verify_task ─▶ passed? ─▶ release_task
20
+ ▲ │
21
+ │ ▼ blocking gaps
22
+ └──────────── self-repair ◀──── next_actions (typed)
23
+ ```
24
+
25
+ ## Step-by-step workflow
26
+
27
+ ### 1. Pick up work
28
+
29
+ ```
30
+ devcouncil_next_task # highest-priority unblocked task (or use a known TASK-ID)
31
+ devcouncil_checkout_task # acquire lease — required before writes or verify
32
+ devcouncil_get_task # scope, planned files, acceptance criteria
33
+ devcouncil_get_prompt # full executor prompt with rigor + context
34
+ ```
35
+
36
+ One agent owns the task at a time. If checkout fails (lease held), do not bypass — pick
37
+ another task or wait for release.
38
+
39
+ ### 2. Implement inside scope
40
+
41
+ - Read with `devcouncil_read_file`; inspect changes with `devcouncil_get_diff`.
42
+ - **Write only through the gate:** `devcouncil_write_file` / `devcouncil_apply_patch`.
43
+ Direct editor writes to out-of-scope or protected paths are rejected.
44
+ - Run tests with `devcouncil_run_command` or `devcouncil_record_command`.
45
+ - Preflight questionable paths with `devcouncil_policy_check_write`.
46
+
47
+ Stay inside the task's **planned files** and **allowed commands**. Do not expand scope
48
+ silently — use `devcouncil_update_task_scope` only when the task genuinely requires it.
49
+
50
+ When the advisor tool is available, consult it before committing to an approach, when
51
+ stuck on a recurring error, and before declaring the task complete.
52
+
53
+ ### 3. Verify
54
+
55
+ ```
56
+ devcouncil_verify_task # deterministic verifier — requires active lease
57
+ ```
58
+
59
+ Returns `passed`, `blocking_gaps`, and `next_actions`. A green test suite is not enough;
60
+ the verifier checks planned-file compliance, orphan diffs, acceptance evidence, diff
61
+ coverage, stub detection, and more.
62
+
63
+ ### 4. Self-repair from next_actions
64
+
65
+ When `passed` is false:
66
+
67
+ ```
68
+ devcouncil_get_next_actions # cheap read of persisted gaps — no re-verify
69
+ ```
70
+
71
+ Each action is typed (`category`: `fix_code`, `add_test`, `fix_verification`, `scope`,
72
+ `security`, `review`, `plan`) with `file`, `line`, and often `suggested_command`.
73
+ Fix each blocking action, then call `devcouncil_verify_task` again. Repeat until clean.
74
+
75
+ Do **not** weaken tests, stub around gaps, or skip verification to force a pass.
76
+
77
+ ### 5. Release
78
+
79
+ ```
80
+ devcouncil_release_task # only after verify passes (or explicit abandon policy)
81
+ ```
82
+
83
+ Report: task ID, what changed, verification result, and any advisory (non-blocking) gaps.
84
+
85
+ ## Alternatives to manual MCP driving
86
+
87
+ | Entry | When |
88
+ |---|---|
89
+ | `/devcouncil:next` | Interactive Claude Code — shells out then instructs MCP loop |
90
+ | `dev go [TASK-ID]` | CLI-driven loop with repair budget |
91
+ | `dev e2e "<goal>" --executor claude` | Full plan → implement → verify automation |
92
+ | Subagent `devcouncil-implementer` | Delegate the entire loop |
93
+
94
+ ## Lite on-ramp (no task graph yet)
95
+
96
+ Before full planning, taste the evidence gate on the current diff:
97
+
98
+ ```bash
99
+ dev check --verify --goal "requirement text" --test "python -m pytest tests/ -q"
100
+ ```
101
+
102
+ Same deterministic verifier as the hero loop. Graduate to `dev plan` once you trust the gate.
103
+
104
+ ## Common mistakes
105
+
106
+ - Editing files before checkout (writes may fail or lack provenance).
107
+ - Calling `devcouncil_verify_task` without a lease (rejected).
108
+ - Declaring done on test pass alone (verifier may report `diff_not_exercised`, stubs, scope gaps).
109
+ - Ignoring `next_actions` categories — branch on `category`, not prose parsing.
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: devcouncil-verification
3
+ title: DevCouncil Verification & Next Actions
4
+ description: Interpret DevCouncil verification results — blocking gaps, typed next_actions, diff coverage, and anti-laziness rigor — and repair until evidence proves the diff.
5
+ triggers:
6
+ keywords: [verification, verify, blocking gap, next action, diff coverage, dev check, dev verify, dev gaps, acceptance evidence, rigor]
7
+ markers: [.devcouncil/config.yaml]
8
+ ---
9
+
10
+ # DevCouncil Verification & Next Actions
11
+
12
+ DevCouncil's verifier is **deterministic** (no extra LLM calls for stub/effort detection).
13
+ It decides pass/fail from evidence: diffs, test output, coverage intersection, and policy checks.
14
+
15
+ ## Verification outputs
16
+
17
+ `devcouncil_verify_task` (or `dev verify`, `dev check --verify`) returns:
18
+
19
+ - **`passed`** — no blocking gaps remain
20
+ - **`blocking_gaps`** — must fix before release
21
+ - **`next_actions`** — typed, machine-routable repair instructions
22
+
23
+ Read persisted state without re-verifying:
24
+
25
+ ```
26
+ devcouncil_get_gaps # gap list (blocking_only filter available)
27
+ devcouncil_get_next_actions # blocking + advisory actions, allowed_next_tools
28
+ devcouncil_get_evidence # command results, test evidence
29
+ devcouncil_get_task_provenance # audit trail: writes, verify runs, diff coverage
30
+ ```
31
+
32
+ ## Next-actions contract
33
+
34
+ Each action includes:
35
+
36
+ | Field | Use |
37
+ |---|---|
38
+ | `gap_id` | Stable identifier for tracking |
39
+ | `gap_type` | e.g. `diff_not_exercised`, `stub_detected`, `orphan_diff` |
40
+ | `category` | Branch on this: `fix_code`, `add_test`, `fix_verification`, `scope`, `security`, `review`, `plan` |
41
+ | `severity` | `high` / `medium` / `low` |
42
+ | `blocking` | Must fix when true |
43
+ | `action` | Human-readable fix instruction |
44
+ | `file` / `line` | Precise location |
45
+ | `suggested_command` | Command to run after fixing |
46
+
47
+ Act on **blocking** actions first. Advisory actions surface quality issues but do not
48
+ block release unless configured to.
49
+
50
+ ## Diff↔coverage gate
51
+
52
+ A passing test suite only counts if it **executed the changed lines**. The verifier
53
+ intersects coverage data with diff hunks. An unexercised diff yields `diff_not_exercised`.
54
+
55
+ - **Signal-first by default** — informational unless `verification.diff_coverage.enforce: true`
56
+ - **Hard tasks** — `verification.rigor.enforce_coverage_on_hard` promotes to blocking
57
+ - **Degrades silently** when coverage tooling or parseable diff is unavailable — never
58
+ blocks correct work for lack of measurement
59
+
60
+ Config (`.devcouncil/config.yaml`):
61
+
62
+ ```yaml
63
+ verification:
64
+ diff_coverage:
65
+ measure: true
66
+ enforce: false
67
+ min_ratio: 0.0
68
+ ```
69
+
70
+ ## Anti-laziness rigor
71
+
72
+ Scales strictness by task **difficulty** (`easy` / `normal` / `hard`):
73
+
74
+ | Gate | Easy/Normal | Hard |
75
+ |---|---|---|
76
+ | Stub/TODO detection | Advisory | Blocking |
77
+ | Effort heuristics (undersized diff) | Advisory | Blocking |
78
+ | Coarse acceptance proof | Advisory | Blocking |
79
+ | Diff coverage | Advisory (unless enforce) | Blocking (default) |
80
+
81
+ Stub markers (`TODO`, `NotImplementedError`, assert-free tests) block on hard tasks unless
82
+ the task mentions scaffolding and the line carries `devcouncil: allow-stub`.
83
+
84
+ Repair runs include a **correction manifest** with prior diff, failing output, and
85
+ non-negotiable rules: never weaken tests, never stub around a gap.
86
+
87
+ ## Repair workflow
88
+
89
+ 1. `devcouncil_get_next_actions` — list blocking items
90
+ 2. Fix each gap (smallest change that closes it)
91
+ 3. Re-run suggested tests via `devcouncil_run_command`
92
+ 4. `devcouncil_verify_task` — repeat until `passed`
93
+ 5. `/devcouncil:repair [TASK-ID]` or `dev repair [TASK-ID]` for CLI-guided repair
94
+
95
+ ## CLI equivalents
96
+
97
+ ```bash
98
+ dev verify [TASK-ID] # full task verification
99
+ dev gaps [TASK-ID] # list gaps
100
+ dev check --verify --test "…" # inline requirement on current diff
101
+ dev report # Requirement→Task→Diff→Evidence coverage
102
+ dev report rigor # tune rigor thresholds from evidence
103
+ ```
104
+
105
+ ## When to call success
106
+
107
+ Only when **`passed: true`** from `devcouncil_verify_task` with zero blocking gaps.
108
+ Report the verification result and cite `file:line` for any remaining advisory findings.
109
+ Never claim done based on test output alone without running verify.