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.
- package/README.md +46 -30
- package/package.json +6 -2
- package/packages/codeintel-grammars/hatch_build.py +43 -0
- package/packages/codeintel-grammars/pyproject.toml +16 -0
- package/packages/codeintel-grammars/src/devcouncil_codeintel_grammars/__init__.py +93 -0
- package/pyproject.toml +99 -4
- package/src/devcouncil/app/config.py +512 -20
- package/src/devcouncil/app/events.py +4 -23
- package/src/devcouncil/app/orchestrator.py +5 -0
- package/src/devcouncil/app/run_context.py +3 -3
- package/src/devcouncil/assets/__init__.py +4 -1
- package/src/devcouncil/assets/vendor/force-graph.min.js +5 -0
- package/src/devcouncil/campaign/__init__.py +71 -0
- package/src/devcouncil/campaign/bloom.py +137 -0
- package/src/devcouncil/campaign/dashboard.py +123 -0
- package/src/devcouncil/campaign/mailbox.py +305 -0
- package/src/devcouncil/campaign/notify.py +91 -0
- package/src/devcouncil/campaign/orchestrator.py +592 -0
- package/src/devcouncil/campaign/prompts/coordinator.md +29 -0
- package/src/devcouncil/campaign/prompts/director.md +21 -0
- package/src/devcouncil/campaign/prompts/protocol.md +46 -0
- package/src/devcouncil/campaign/prompts/reviewer.md +24 -0
- package/src/devcouncil/campaign/prompts/worker.md +24 -0
- package/src/devcouncil/campaign/roles.py +202 -0
- package/src/devcouncil/campaign/watcher.py +153 -0
- package/src/devcouncil/cli/commands/agents.py +24 -17
- package/src/devcouncil/cli/commands/artifacts.py +36 -27
- package/src/devcouncil/cli/commands/ast.py +12 -3
- package/src/devcouncil/cli/commands/baseline.py +21 -12
- package/src/devcouncil/cli/commands/boot.py +218 -0
- package/src/devcouncil/cli/commands/campaign.py +302 -0
- package/src/devcouncil/cli/commands/check.py +225 -12
- package/src/devcouncil/cli/commands/config.py +221 -74
- package/src/devcouncil/cli/commands/cost.py +137 -28
- package/src/devcouncil/cli/commands/dashboard.py +12 -4
- package/src/devcouncil/cli/commands/debug_cmd.py +249 -0
- package/src/devcouncil/cli/commands/design.py +27 -17
- package/src/devcouncil/cli/commands/doctor.py +790 -8
- package/src/devcouncil/cli/commands/evidence.py +41 -20
- package/src/devcouncil/cli/commands/export.py +73 -0
- package/src/devcouncil/cli/commands/gaps.py +175 -0
- package/src/devcouncil/cli/commands/gated_write.py +76 -0
- package/src/devcouncil/cli/commands/go.py +220 -68
- package/src/devcouncil/cli/commands/graph_cmd.py +1192 -0
- package/src/devcouncil/cli/commands/handoff.py +45 -34
- package/src/devcouncil/cli/commands/hook.py +630 -85
- package/src/devcouncil/cli/commands/init.py +89 -30
- package/src/devcouncil/cli/commands/integrate.py +296 -1385
- package/src/devcouncil/cli/commands/lease.py +120 -0
- package/src/devcouncil/cli/commands/logs.py +12 -5
- package/src/devcouncil/cli/commands/lsp.py +40 -5
- package/src/devcouncil/cli/commands/map.py +317 -74
- package/src/devcouncil/cli/commands/mcp_server.py +12 -2
- package/src/devcouncil/cli/commands/okf.py +44 -6
- package/src/devcouncil/cli/commands/plan.py +184 -69
- package/src/devcouncil/cli/commands/prompt.py +26 -17
- package/src/devcouncil/cli/commands/provenance.py +79 -0
- package/src/devcouncil/cli/commands/repair.py +60 -49
- package/src/devcouncil/cli/commands/report.py +148 -40
- package/src/devcouncil/cli/commands/requirements.py +104 -0
- package/src/devcouncil/cli/commands/reset_demo_state.py +13 -4
- package/src/devcouncil/cli/commands/rollback.py +46 -35
- package/src/devcouncil/cli/commands/run.py +173 -8
- package/src/devcouncil/cli/commands/runs.py +298 -68
- package/src/devcouncil/cli/commands/scaffold.py +33 -12
- package/src/devcouncil/cli/commands/semantic.py +29 -14
- package/src/devcouncil/cli/commands/setup.py +103 -93
- package/src/devcouncil/cli/commands/shell.py +51 -42
- package/src/devcouncil/cli/commands/show.py +56 -42
- package/src/devcouncil/cli/commands/skills.py +29 -20
- package/src/devcouncil/cli/commands/status.py +80 -67
- package/src/devcouncil/cli/commands/task_gate.py +295 -0
- package/src/devcouncil/cli/commands/tasks.py +248 -19
- package/src/devcouncil/cli/commands/trace.py +14 -8
- package/src/devcouncil/cli/commands/verify.py +33 -8
- package/src/devcouncil/cli/commands/version.py +14 -6
- package/src/devcouncil/cli/commands/watch.py +49 -30
- package/src/devcouncil/cli/commands/watch_fs.py +30 -19
- package/src/devcouncil/cli/commands/wiki.py +278 -0
- package/src/devcouncil/cli/main.py +58 -1
- package/src/devcouncil/codeintel/__init__.py +16 -0
- package/src/devcouncil/codeintel/build_control.py +429 -0
- package/src/devcouncil/codeintel/build_worker.py +78 -0
- package/src/devcouncil/codeintel/debug/__init__.py +17 -0
- package/src/devcouncil/codeintel/debug/broker.py +114 -0
- package/src/devcouncil/codeintel/debug/broker_client.py +61 -0
- package/src/devcouncil/codeintel/debug/consent.py +36 -0
- package/src/devcouncil/codeintel/debug/discovery.py +132 -0
- package/src/devcouncil/codeintel/debug/fingerprint.py +85 -0
- package/src/devcouncil/codeintel/debug/protocol.py +259 -0
- package/src/devcouncil/codeintel/debug/python_trace_runner.py +81 -0
- package/src/devcouncil/codeintel/debug/session.py +238 -0
- package/src/devcouncil/codeintel/debug/tracing.py +201 -0
- package/src/devcouncil/codeintel/languages/__init__.py +17 -0
- package/src/devcouncil/codeintel/languages/generic_extractor.py +236 -0
- package/src/devcouncil/codeintel/languages/registry.py +149 -0
- package/src/devcouncil/codeintel/languages/workers.py +245 -0
- package/src/devcouncil/codeintel/query/__init__.py +5 -0
- package/src/devcouncil/codeintel/query/engine.py +289 -0
- package/src/devcouncil/codeintel/resolution/__init__.py +6 -0
- package/src/devcouncil/codeintel/resolution/abstract_state.py +301 -0
- package/src/devcouncil/codeintel/resolution/frameworks/__init__.py +33 -0
- package/src/devcouncil/codeintel/resolution/frameworks/base.py +46 -0
- package/src/devcouncil/codeintel/resolution/frameworks/di.py +56 -0
- package/src/devcouncil/codeintel/resolution/frameworks/events.py +45 -0
- package/src/devcouncil/codeintel/resolution/frameworks/routes.py +88 -0
- package/src/devcouncil/codeintel/resolution/semantic.py +887 -0
- package/src/devcouncil/codeintel/service.py +104 -0
- package/src/devcouncil/codeintel/store/__init__.py +15 -0
- package/src/devcouncil/codeintel/store/sqlite.py +1565 -0
- package/src/devcouncil/codeintel/sync/__init__.py +19 -0
- package/src/devcouncil/codeintel/sync/coordinator.py +430 -0
- package/src/devcouncil/codeintel/sync/incremental.py +484 -0
- package/src/devcouncil/codeintel/sync/lease.py +96 -0
- package/src/devcouncil/codeintel/sync/scope.py +98 -0
- package/src/devcouncil/council/__init__.py +4 -0
- package/src/devcouncil/council/prompts/__init__.py +4 -0
- package/src/devcouncil/domain/checkpoint_refs.py +17 -0
- package/src/devcouncil/domain/evidence.py +1 -0
- package/src/devcouncil/domain/gap.py +10 -0
- package/src/devcouncil/domain/requirement.py +5 -1
- package/src/devcouncil/domain/task.py +43 -2
- package/src/devcouncil/execution/checkpoints.py +25 -31
- package/src/devcouncil/execution/context_builder.py +15 -44
- package/src/devcouncil/execution/fs_watcher.py +64 -0
- package/src/devcouncil/execution/gated_write.py +203 -0
- package/src/devcouncil/execution/handoff.py +2 -1
- package/src/devcouncil/execution/hook_policy.py +19 -5
- package/src/devcouncil/execution/lease_ops.py +177 -0
- package/src/devcouncil/execution/lease_validation.py +71 -0
- package/src/devcouncil/execution/patch.py +3 -0
- package/src/devcouncil/execution/permissions.py +1 -0
- package/src/devcouncil/execution/policy_engine.py +205 -10
- package/src/devcouncil/execution/prompt_builder.py +278 -33
- package/src/devcouncil/execution/run_trace.py +356 -0
- package/src/devcouncil/execution/shell_session.py +46 -5
- package/src/devcouncil/execution/stop_gate.py +746 -0
- package/src/devcouncil/execution/stop_gate_history.py +113 -0
- package/src/devcouncil/execution/stop_gate_state.py +54 -0
- package/src/devcouncil/execution/stop_gate_verify_cache.py +69 -0
- package/src/devcouncil/execution/task_gate_ops.py +590 -0
- package/src/devcouncil/execution/task_runner.py +19 -0
- package/src/devcouncil/executors/advisor_tool.py +315 -0
- package/src/devcouncil/executors/agent_registry.py +125 -17
- package/src/devcouncil/executors/claude_sdk.py +376 -0
- package/src/devcouncil/executors/coding_cli.py +724 -25
- package/src/devcouncil/executors/mini_swe.py +50 -8
- package/src/devcouncil/executors/native/agent.py +224 -19
- package/src/devcouncil/executors/openhands.py +50 -8
- package/src/devcouncil/executors/transient_retry.py +99 -0
- package/src/devcouncil/gating/checks/clean_git.py +5 -2
- package/src/devcouncil/gating/checks/planned_files_check.py +38 -11
- package/src/devcouncil/gating/checks/secret_scan_check.py +2 -2
- package/src/devcouncil/gating/policy.py +46 -2
- package/src/devcouncil/indexing/ast_matcher.py +41 -4
- package/src/devcouncil/indexing/graph/__init__.py +78 -0
- package/src/devcouncil/indexing/graph/api_routes.py +522 -0
- package/src/devcouncil/indexing/graph/build.py +862 -0
- package/src/devcouncil/indexing/graph/cache.py +329 -0
- package/src/devcouncil/indexing/graph/communities.py +28 -0
- package/src/devcouncil/indexing/graph/cypher.py +107 -0
- package/src/devcouncil/indexing/graph/embeddings.py +194 -0
- package/src/devcouncil/indexing/graph/export.py +381 -0
- package/src/devcouncil/indexing/graph/export_links.py +81 -0
- package/src/devcouncil/indexing/graph/extract_python.py +307 -0
- package/src/devcouncil/indexing/graph/extract_ts.py +1205 -0
- package/src/devcouncil/indexing/graph/intel.py +668 -0
- package/src/devcouncil/indexing/graph/liveness.py +992 -0
- package/src/devcouncil/indexing/graph/okf_export.py +65 -0
- package/src/devcouncil/indexing/graph/pdg/__init__.py +67 -0
- package/src/devcouncil/indexing/graph/pdg/build.py +11 -0
- package/src/devcouncil/indexing/graph/pdg/cdg.py +41 -0
- package/src/devcouncil/indexing/graph/pdg/cfg.py +199 -0
- package/src/devcouncil/indexing/graph/pdg/query.py +21 -0
- package/src/devcouncil/indexing/graph/pdg/reaching_def.py +126 -0
- package/src/devcouncil/indexing/graph/pdg/schema.py +253 -0
- package/src/devcouncil/indexing/graph/pdg/taint.py +154 -0
- package/src/devcouncil/indexing/graph/query.py +302 -0
- package/src/devcouncil/indexing/graph/resolve.py +1020 -0
- package/src/devcouncil/indexing/graph/schema.py +103 -0
- package/src/devcouncil/indexing/graph_index.py +20 -29
- package/src/devcouncil/indexing/lsp.py +57 -25
- package/src/devcouncil/indexing/lsp_client.py +577 -0
- package/src/devcouncil/indexing/map_artifacts.py +355 -0
- package/src/devcouncil/indexing/map_refresh.py +141 -0
- package/src/devcouncil/indexing/repo_mapper.py +1509 -138
- package/src/devcouncil/indexing/semantic_index.py +12 -6
- package/src/devcouncil/indexing/subsystem_map.py +163 -0
- package/src/devcouncil/indexing/ts_imports.py +343 -0
- package/src/devcouncil/indexing/viz.py +960 -0
- package/src/devcouncil/indexing/walk.py +52 -0
- package/src/devcouncil/indexing/wiring.py +1776 -0
- package/src/devcouncil/integrations/actions.py +27 -4
- package/src/devcouncil/integrations/check.py +211 -16
- package/src/devcouncil/integrations/claude_assets.py +209 -12
- package/src/devcouncil/integrations/clients/__init__.py +1 -0
- package/src/devcouncil/integrations/clients/aider.py +52 -0
- package/src/devcouncil/integrations/clients/antigravity.py +87 -0
- package/src/devcouncil/integrations/clients/claude.py +339 -0
- package/src/devcouncil/integrations/clients/codex.py +39 -0
- package/src/devcouncil/integrations/clients/common.py +332 -0
- package/src/devcouncil/integrations/clients/cursor.py +164 -0
- package/src/devcouncil/integrations/clients/gemini.py +49 -0
- package/src/devcouncil/integrations/clients/grok.py +105 -0
- package/src/devcouncil/integrations/clients/hooks.py +500 -0
- package/src/devcouncil/integrations/clients/opencode.py +96 -0
- package/src/devcouncil/integrations/clients/warp.py +75 -0
- package/src/devcouncil/integrations/code_review_graph.py +2 -2
- package/src/devcouncil/integrations/github.py +73 -7
- package/src/devcouncil/integrations/integration_cli.py +197 -0
- package/src/devcouncil/integrations/mcp/handlers/__init__.py +1 -0
- package/src/devcouncil/integrations/mcp/handlers/ast_lsp.py +77 -0
- package/src/devcouncil/integrations/mcp/handlers/checkout.py +50 -0
- package/src/devcouncil/integrations/mcp/handlers/cli_gate.py +43 -0
- package/src/devcouncil/integrations/mcp/handlers/codeintel.py +182 -0
- package/src/devcouncil/integrations/mcp/handlers/debug.py +236 -0
- package/src/devcouncil/integrations/mcp/handlers/evidence.py +70 -0
- package/src/devcouncil/integrations/mcp/handlers/git.py +99 -0
- package/src/devcouncil/integrations/mcp/handlers/graph.py +36 -0
- package/src/devcouncil/integrations/mcp/handlers/handoff.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/knowledge.py +30 -0
- package/src/devcouncil/integrations/mcp/handlers/lease.py +70 -0
- package/src/devcouncil/integrations/mcp/handlers/live.py +108 -0
- package/src/devcouncil/integrations/mcp/handlers/map.py +676 -0
- package/src/devcouncil/integrations/mcp/handlers/next_task.py +35 -0
- package/src/devcouncil/integrations/mcp/handlers/policy.py +80 -0
- package/src/devcouncil/integrations/mcp/handlers/prompts.py +168 -0
- package/src/devcouncil/integrations/mcp/handlers/provenance.py +101 -0
- package/src/devcouncil/integrations/mcp/handlers/read.py +74 -0
- package/src/devcouncil/integrations/mcp/handlers/router_cache.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/run.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/runs.py +84 -0
- package/src/devcouncil/integrations/mcp/handlers/scope.py +56 -0
- package/src/devcouncil/integrations/mcp/handlers/status.py +114 -0
- package/src/devcouncil/integrations/mcp/handlers/task.py +100 -0
- package/src/devcouncil/integrations/mcp/handlers/tool_specs.py +904 -0
- package/src/devcouncil/integrations/mcp/handlers/trace.py +65 -0
- package/src/devcouncil/integrations/mcp/handlers/verify.py +45 -0
- package/src/devcouncil/integrations/mcp/handlers/wiki.py +60 -0
- package/src/devcouncil/integrations/mcp/handlers/write.py +69 -0
- package/src/devcouncil/integrations/mcp/server.py +265 -2391
- package/src/devcouncil/integrations/mcp/util.py +303 -0
- package/src/devcouncil/integrations/setup.py +152 -0
- package/src/devcouncil/knowledge/fetch.py +4 -0
- package/src/devcouncil/knowledge/knowledge_select.py +38 -0
- package/src/devcouncil/knowledge/okf.py +2 -1
- package/src/devcouncil/knowledge/resource_discovery.py +40 -0
- package/src/devcouncil/knowledge/wiki.py +643 -0
- package/src/devcouncil/knowledge/wiki_read.py +87 -0
- package/src/devcouncil/live/cards.py +7 -7
- package/src/devcouncil/live/models.py +4 -1
- package/src/devcouncil/live/reviewer.py +90 -11
- package/src/devcouncil/live/signals.py +4 -2
- package/src/devcouncil/live/tasks.py +12 -3
- package/src/devcouncil/live/transcripts.py +69 -2
- package/src/devcouncil/llm/cache.py +5 -6
- package/src/devcouncil/llm/model_defaults.yaml +10 -10
- package/src/devcouncil/llm/provider.py +647 -73
- package/src/devcouncil/llm/router.py +271 -46
- package/src/devcouncil/llm/semantic_bridge.py +614 -0
- package/src/devcouncil/optimization/gepa_agent.py +6 -4
- package/src/devcouncil/optimization/skillopt.py +9 -5
- package/src/devcouncil/planning/arbiter_service.py +12 -3
- package/src/devcouncil/planning/correction_manifest.py +107 -10
- package/src/devcouncil/planning/plan_difficulty.py +69 -0
- package/src/devcouncil/planning/plan_service.py +5 -2
- package/src/devcouncil/planning/planned_files_reconcile.py +191 -0
- package/src/devcouncil/planning/prompt_enhancer_service.py +6 -5
- package/src/devcouncil/planning/question_conversion.py +56 -0
- package/src/devcouncil/planning/spec_service.py +9 -3
- package/src/devcouncil/repo/ci_scaffold.py +197 -1
- package/src/devcouncil/repo/gitignore.py +1 -2
- package/src/devcouncil/reporting/evidence_export.py +124 -0
- package/src/devcouncil/reporting/evidence_html.py +210 -0
- package/src/devcouncil/reporting/json_report.py +16 -12
- package/src/devcouncil/reporting/markdown_report.py +38 -9
- package/src/devcouncil/reporting/mcp_resources.py +142 -0
- package/src/devcouncil/reporting/report_builder.py +40 -4
- package/src/devcouncil/reporting/task_provenance.py +42 -0
- package/src/devcouncil/reporting/verdict.py +75 -0
- package/src/devcouncil/skills/library/README.md +1 -0
- package/src/devcouncil/skills/library/devcouncil-hero-loop.md +109 -0
- package/src/devcouncil/skills/library/devcouncil-verification.md +109 -0
- package/src/devcouncil/skills/library/devcouncil.md +93 -0
- package/src/devcouncil/skills/registry.py +43 -12
- package/src/devcouncil/storage/db.py +57 -11
- package/src/devcouncil/storage/models.py +6 -0
- package/src/devcouncil/storage/native.py +5 -3
- package/src/devcouncil/storage/repositories.py +50 -18
- package/src/devcouncil/telemetry/context.py +28 -0
- package/src/devcouncil/telemetry/cost.py +4 -5
- package/src/devcouncil/telemetry/logging_setup.py +78 -11
- package/src/devcouncil/telemetry/model_pricing.yaml +7 -0
- package/src/devcouncil/telemetry/stages.py +27 -2
- package/src/devcouncil/telemetry/tracker.py +50 -13
- package/src/devcouncil/ui/dashboard.py +120 -8
- package/src/devcouncil/utils/fsio.py +58 -0
- package/src/devcouncil/utils/git_snapshot.py +112 -0
- package/src/devcouncil/utils/json_persist.py +53 -0
- package/src/devcouncil/utils/proc.py +89 -0
- package/src/devcouncil/verification/acceptance_compiler.py +36 -13
- package/src/devcouncil/verification/ad_hoc_check.py +95 -3
- package/src/devcouncil/verification/checks/__init__.py +41 -0
- package/src/devcouncil/verification/checks/acceptance.py +39 -0
- package/src/devcouncil/verification/checks/acceptance_corpus.py +194 -0
- package/src/devcouncil/verification/checks/acceptance_evidence.py +239 -0
- package/src/devcouncil/verification/checks/command_evidence.py +148 -0
- package/src/devcouncil/verification/checks/compiled_acceptance.py +179 -0
- package/src/devcouncil/verification/checks/corpus_stale.py +124 -0
- package/src/devcouncil/verification/checks/corpus_verification.py +9 -0
- package/src/devcouncil/verification/checks/dead_symbols.py +360 -0
- package/src/devcouncil/verification/checks/diff_coverage_gate.py +101 -0
- package/src/devcouncil/verification/checks/doc_code_ref.py +79 -0
- package/src/devcouncil/verification/checks/liveness_ratchet.py +336 -0
- package/src/devcouncil/verification/checks/orphan_diff.py +104 -0
- package/src/devcouncil/verification/checks/planned_files.py +98 -0
- package/src/devcouncil/verification/checks/semantic_diff.py +241 -0
- package/src/devcouncil/verification/checks/stale_map.py +80 -0
- package/src/devcouncil/verification/checks/stub_scan.py +71 -0
- package/src/devcouncil/verification/checks/subsystem_boundary.py +103 -0
- package/src/devcouncil/verification/checks/wiring.py +216 -0
- package/src/devcouncil/verification/claims/__init__.py +23 -0
- package/src/devcouncil/verification/claims/checks.py +395 -0
- package/src/devcouncil/verification/claims/mapper.py +168 -0
- package/src/devcouncil/verification/claims/models.py +39 -0
- package/src/devcouncil/verification/claims/transcript.py +92 -0
- package/src/devcouncil/verification/claims/verdict.py +88 -0
- package/src/devcouncil/verification/command_evidence.py +170 -0
- package/src/devcouncil/verification/command_malformation.py +147 -0
- package/src/devcouncil/verification/command_runner.py +164 -0
- package/src/devcouncil/verification/coverage_measurement.py +292 -0
- package/src/devcouncil/verification/diff_coverage.py +151 -0
- package/src/devcouncil/verification/difficulty.py +296 -0
- package/src/devcouncil/verification/effort_heuristics.py +178 -0
- package/src/devcouncil/verification/gap_ids.py +63 -0
- package/src/devcouncil/verification/gate_cache.py +194 -0
- package/src/devcouncil/verification/gate_selector.py +344 -0
- package/src/devcouncil/verification/git_diff_fallback.py +272 -0
- package/src/devcouncil/verification/implementation_reviewer.py +13 -0
- package/src/devcouncil/verification/incremental_check.py +241 -0
- package/src/devcouncil/verification/next_actions.py +60 -1
- package/src/devcouncil/verification/rigor_analytics.py +130 -0
- package/src/devcouncil/verification/sandbox.py +38 -11
- package/src/devcouncil/verification/stub_detector.py +369 -0
- package/src/devcouncil/verification/test_resolver.py +67 -1
- package/src/devcouncil/verification/verifier.py +137 -1666
- package/src/devcouncil/verification/verify_orchestration.py +610 -0
- package/src/devcouncil/verification/verify_setup.py +176 -0
- package/src/devcouncil/verification/wiki_refresh.py +208 -0
- package/src/semantic_layer/__init__.py +58 -0
- package/src/semantic_layer/benchmark.py +75 -0
- package/src/semantic_layer/cache.py +290 -0
- package/src/semantic_layer/compressor.py +137 -0
- package/src/semantic_layer/config.py +75 -0
- package/src/semantic_layer/embeddings.py +69 -0
- package/src/semantic_layer/llm_backends.py +99 -0
- package/src/semantic_layer/pipeline.py +111 -0
- package/src/semantic_layer/router.py +128 -0
- package/src/semantic_layer/tuner.py +72 -0
- package/uv.lock +973 -9
- package/src/devcouncil/artifacts/migrations.py +0 -20
- package/src/devcouncil/artifacts/schemas.py +0 -23
- package/src/devcouncil/artifacts/serializer.py +0 -21
- package/src/devcouncil/integrations/gitnexus.py +0 -70
- 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(
|
|
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
|
|
67
|
+
md_output += "| Requirement | Task | Task status |\n"
|
|
60
68
|
md_output += "|---|---|---|\n"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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(
|
|
12
|
-
|
|
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
|
|
16
|
-
|
|
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.
|