devcouncil 0.3.1 → 0.4.1
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 +92 -33
- 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 +56 -40
- 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 +964 -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 +281 -0
- package/src/devcouncil/integrations/mcp/handlers/graph.py +34 -0
- package/src/devcouncil/integrations/mcp/handlers/handoff.py +53 -0
- package/src/devcouncil/integrations/mcp/handlers/knowledge.py +28 -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 +87 -0
- package/src/devcouncil/integrations/mcp/handlers/read.py +103 -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 +69 -0
- package/src/devcouncil/integrations/mcp/handlers/scope.py +56 -0
- package/src/devcouncil/integrations/mcp/handlers/status.py +199 -0
- package/src/devcouncil/integrations/mcp/handlers/task.py +88 -0
- package/src/devcouncil/integrations/mcp/handlers/tool_specs.py +922 -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 +53 -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 +325 -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/summary.py +21 -3
- 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
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
"""Core verify_task gate orchestration extracted from Verifier."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Any, List, Literal, TYPE_CHECKING, cast
|
|
7
|
+
|
|
8
|
+
from devcouncil.domain.evidence import CommandResult, DiffEvidence
|
|
9
|
+
from devcouncil.domain.gap import Gap
|
|
10
|
+
from devcouncil.domain.requirement import Requirement
|
|
11
|
+
from devcouncil.domain.task import Task
|
|
12
|
+
from devcouncil.live.cards import unresolved_blocking_cards
|
|
13
|
+
from devcouncil.telemetry.stages import log_step
|
|
14
|
+
from devcouncil.verification.checks.acceptance_evidence import map_acceptance_criteria_evidence
|
|
15
|
+
from devcouncil.verification.checks.compiled_acceptance import (
|
|
16
|
+
promote_demoted_failures_when_compiler_incomplete,
|
|
17
|
+
run_compiled_acceptance_checks,
|
|
18
|
+
)
|
|
19
|
+
from devcouncil.verification.checks.command_evidence import run_verification_commands
|
|
20
|
+
from devcouncil.verification.checks.diff_coverage_gate import run_diff_coverage_gate
|
|
21
|
+
from devcouncil.verification.checks.planned_files import (
|
|
22
|
+
detect_dependency_risk_gaps,
|
|
23
|
+
detect_no_work_gap,
|
|
24
|
+
detect_planned_file_gaps,
|
|
25
|
+
)
|
|
26
|
+
from devcouncil.verification.checks.orphan_diff import detect_orphan_diff_gaps
|
|
27
|
+
from devcouncil.verification.checks.stub_scan import detect_stub_gaps
|
|
28
|
+
from devcouncil.verification.effort_heuristics import detect_effort_anomalies
|
|
29
|
+
from devcouncil.verification.verify_setup import VerifyRunContext
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from devcouncil.verification.verifier import Verifier
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _verification_mode_label(evidence_to_save: list[Any], *, compiler_active: bool) -> str:
|
|
38
|
+
"""Derive reported verification mode from TestEvidence proof modes, not compiler presence."""
|
|
39
|
+
from devcouncil.domain.evidence import TestEvidence
|
|
40
|
+
|
|
41
|
+
test_evidence = [item for item in evidence_to_save if isinstance(item, TestEvidence)]
|
|
42
|
+
if not test_evidence:
|
|
43
|
+
return "compiled" if compiler_active else "coarse"
|
|
44
|
+
modes = {item.mode for item in test_evidence if item.mode}
|
|
45
|
+
if modes & {"compiled", "vote"}:
|
|
46
|
+
return "compiled"
|
|
47
|
+
if "coarse" in modes:
|
|
48
|
+
return "compiled" if compiler_active else "coarse"
|
|
49
|
+
return "compiled" if compiler_active else "coarse"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
async def run_verify_orchestration(
|
|
53
|
+
verifier: "Verifier",
|
|
54
|
+
task: Task,
|
|
55
|
+
requirements: List[Requirement],
|
|
56
|
+
ctx: VerifyRunContext,
|
|
57
|
+
compile_future: Any,
|
|
58
|
+
review_future: Any,
|
|
59
|
+
) -> tuple[list[Gap], list[Any]]:
|
|
60
|
+
"""Run all verification gates and return gaps plus evidence to persist."""
|
|
61
|
+
gaps: list[Gap] = []
|
|
62
|
+
evidence_to_save: list[Any] = []
|
|
63
|
+
_cfg = ctx.config
|
|
64
|
+
rigor = ctx.rigor
|
|
65
|
+
ac_repair_attempts = ctx.ac_repair_attempts
|
|
66
|
+
changed_files = ctx.changed_files
|
|
67
|
+
diff_content = ctx.diff_content
|
|
68
|
+
diff_empty = ctx.diff_empty
|
|
69
|
+
coverage_measured = False
|
|
70
|
+
coverage_skipped_reason: str | None = None
|
|
71
|
+
|
|
72
|
+
work_present = (not diff_empty) or verifier._task_produced_changes(task.id)
|
|
73
|
+
|
|
74
|
+
no_work_gap = detect_no_work_gap(
|
|
75
|
+
task=task,
|
|
76
|
+
work_present=work_present,
|
|
77
|
+
next_gap_id=verifier._next_gap_id,
|
|
78
|
+
)
|
|
79
|
+
if no_work_gap is not None:
|
|
80
|
+
gaps.append(no_work_gap)
|
|
81
|
+
|
|
82
|
+
if diff_content:
|
|
83
|
+
added_files, deleted_files = verifier._classify_change_paths(changed_files)
|
|
84
|
+
diff_ev = DiffEvidence(
|
|
85
|
+
task_id=task.id,
|
|
86
|
+
changed_files=changed_files,
|
|
87
|
+
added_files=added_files,
|
|
88
|
+
deleted_files=deleted_files,
|
|
89
|
+
diff_summary=f"Diff captured for {len(changed_files)} files.",
|
|
90
|
+
)
|
|
91
|
+
evidence_to_save.append(diff_ev)
|
|
92
|
+
|
|
93
|
+
planned_paths = {pf.path for pf in task.planned_files}
|
|
94
|
+
gaps.extend(detect_planned_file_gaps(
|
|
95
|
+
task=task,
|
|
96
|
+
changed_files=changed_files,
|
|
97
|
+
next_gap_id=verifier._next_gap_id,
|
|
98
|
+
))
|
|
99
|
+
|
|
100
|
+
gaps.extend(detect_orphan_diff_gaps(
|
|
101
|
+
task=task,
|
|
102
|
+
changed_files=changed_files,
|
|
103
|
+
planned_paths=planned_paths,
|
|
104
|
+
diff_content=diff_content,
|
|
105
|
+
project_root=verifier.project_root,
|
|
106
|
+
get_untracked_files=verifier._get_untracked_files,
|
|
107
|
+
next_gap_id=verifier._next_gap_id,
|
|
108
|
+
classify_fn=verifier._classify_change_paths,
|
|
109
|
+
))
|
|
110
|
+
|
|
111
|
+
log_step(
|
|
112
|
+
"verify/3: planned-file and orphan-diff checks",
|
|
113
|
+
project_root=verifier.project_root,
|
|
114
|
+
task_id=task.id,
|
|
115
|
+
gaps_so_far=len(gaps),
|
|
116
|
+
)
|
|
117
|
+
gaps.extend(verifier._check_semantic_diff(task, requirements))
|
|
118
|
+
|
|
119
|
+
gaps.extend(detect_dependency_risk_gaps(
|
|
120
|
+
task=task,
|
|
121
|
+
changed_files=changed_files,
|
|
122
|
+
planned_paths=planned_paths,
|
|
123
|
+
next_gap_id=verifier._next_gap_id,
|
|
124
|
+
))
|
|
125
|
+
|
|
126
|
+
# Advisory architecture-drift gate: flag edits that cross non-neighbor subsystem
|
|
127
|
+
# boundaries the plan never declared. Non-blocking by default, reads only the repo
|
|
128
|
+
# map, and degrades to a no-op when the map is absent/stale.
|
|
129
|
+
boundary_cfg = getattr(getattr(_cfg, "verification", None), "subsystem_boundary", None)
|
|
130
|
+
if changed_files and (boundary_cfg is None or boundary_cfg.enabled):
|
|
131
|
+
from devcouncil.verification.checks.subsystem_boundary import (
|
|
132
|
+
detect_subsystem_boundary_gaps,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
gaps.extend(detect_subsystem_boundary_gaps(
|
|
136
|
+
task=task,
|
|
137
|
+
changed_files=changed_files,
|
|
138
|
+
repo_map=verifier._load_repo_map(),
|
|
139
|
+
next_gap_id=verifier._next_gap_id,
|
|
140
|
+
blocking=bool(getattr(boundary_cfg, "blocking", False)),
|
|
141
|
+
))
|
|
142
|
+
|
|
143
|
+
# Stale-map gate: advisory on easy/normal, blocking on hard. Runs before
|
|
144
|
+
# map-consuming gates so agents refresh before trusting neighbors/dependents.
|
|
145
|
+
if rigor.stale_map_enabled:
|
|
146
|
+
try:
|
|
147
|
+
from devcouncil.verification.checks.stale_map import detect_stale_map_gaps
|
|
148
|
+
|
|
149
|
+
gaps.extend(detect_stale_map_gaps(
|
|
150
|
+
task=task,
|
|
151
|
+
project_root=verifier.project_root,
|
|
152
|
+
next_gap_id=verifier._next_gap_id,
|
|
153
|
+
stale_map_enabled=rigor.stale_map_enabled,
|
|
154
|
+
stale_map_blocking=rigor.stale_map_blocking,
|
|
155
|
+
repo_map=verifier._load_repo_map(),
|
|
156
|
+
))
|
|
157
|
+
except Exception:
|
|
158
|
+
logger.warning("stale-map gate failed; skipping", exc_info=True)
|
|
159
|
+
if rigor.stale_map_blocking:
|
|
160
|
+
gaps.append(Gap(
|
|
161
|
+
id=verifier._next_gap_id(task.id, "QGFAIL-stale_map"),
|
|
162
|
+
severity="medium",
|
|
163
|
+
gap_type="quality_gate_failed",
|
|
164
|
+
task_id=task.id,
|
|
165
|
+
description="Stale-map verification gate crashed; results are incomplete.",
|
|
166
|
+
evidence=["gate:stale_map"],
|
|
167
|
+
recommended_fix="Re-run verify after fixing the gate error; do not treat this pass as proven.",
|
|
168
|
+
blocking=False,
|
|
169
|
+
))
|
|
170
|
+
|
|
171
|
+
if rigor.corpus_stale_enabled or rigor.doc_code_ref_enabled:
|
|
172
|
+
try:
|
|
173
|
+
from devcouncil.verification.checks.corpus_stale import (
|
|
174
|
+
detect_corpus_stale_gaps,
|
|
175
|
+
refresh_corpus_on_verify_if_needed,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
refresh_corpus_on_verify_if_needed(verifier.project_root)
|
|
179
|
+
gaps.extend(detect_corpus_stale_gaps(
|
|
180
|
+
task=task,
|
|
181
|
+
project_root=verifier.project_root,
|
|
182
|
+
changed_files=changed_files,
|
|
183
|
+
next_gap_id=verifier._next_gap_id,
|
|
184
|
+
corpus_stale_enabled=rigor.corpus_stale_enabled,
|
|
185
|
+
corpus_stale_blocking=rigor.corpus_stale_blocking,
|
|
186
|
+
))
|
|
187
|
+
except Exception:
|
|
188
|
+
logger.warning("corpus-stale gate failed; skipping", exc_info=True)
|
|
189
|
+
try:
|
|
190
|
+
from devcouncil.verification.checks.doc_code_ref import detect_doc_code_ref_gaps
|
|
191
|
+
|
|
192
|
+
gaps.extend(detect_doc_code_ref_gaps(
|
|
193
|
+
task=task,
|
|
194
|
+
project_root=verifier.project_root,
|
|
195
|
+
changed_files=changed_files,
|
|
196
|
+
diff_content=diff_content,
|
|
197
|
+
next_gap_id=verifier._next_gap_id,
|
|
198
|
+
doc_code_ref_enabled=rigor.doc_code_ref_enabled,
|
|
199
|
+
doc_code_ref_blocking=rigor.doc_code_ref_blocking,
|
|
200
|
+
))
|
|
201
|
+
except Exception:
|
|
202
|
+
logger.warning("doc-code-ref gate failed; skipping", exc_info=True)
|
|
203
|
+
if rigor.acceptance_corpus_enabled:
|
|
204
|
+
try:
|
|
205
|
+
from devcouncil.verification.checks.acceptance_corpus import (
|
|
206
|
+
detect_acceptance_corpus_gaps,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
gaps.extend(detect_acceptance_corpus_gaps(
|
|
210
|
+
task=task,
|
|
211
|
+
requirements=requirements,
|
|
212
|
+
project_root=verifier.project_root,
|
|
213
|
+
changed_files=changed_files,
|
|
214
|
+
diff_content=diff_content,
|
|
215
|
+
next_gap_id=verifier._next_gap_id,
|
|
216
|
+
acceptance_corpus_enabled=rigor.acceptance_corpus_enabled,
|
|
217
|
+
acceptance_corpus_blocking=rigor.acceptance_corpus_blocking,
|
|
218
|
+
))
|
|
219
|
+
except Exception:
|
|
220
|
+
logger.warning("acceptance-corpus gate failed; skipping", exc_info=True)
|
|
221
|
+
|
|
222
|
+
log_step(
|
|
223
|
+
"verify/4: semantic diff and dependency checks",
|
|
224
|
+
project_root=verifier.project_root,
|
|
225
|
+
task_id=task.id,
|
|
226
|
+
gaps_so_far=len(gaps),
|
|
227
|
+
)
|
|
228
|
+
gaps.extend(detect_stub_gaps(
|
|
229
|
+
task=task,
|
|
230
|
+
diff_content=diff_content,
|
|
231
|
+
project_root=verifier.project_root,
|
|
232
|
+
stub_enabled=rigor.stub_enabled,
|
|
233
|
+
stub_blocking=rigor.stub_blocking,
|
|
234
|
+
next_gap_id=verifier._next_gap_id,
|
|
235
|
+
))
|
|
236
|
+
|
|
237
|
+
# Unwired-file + dead-symbol gates (liveness): run right after placeholder scan so
|
|
238
|
+
# reachability findings sit next to incomplete-code findings in the repair loop.
|
|
239
|
+
# Failures on hard rigor emit an advisory quality_gate_failed so agents see
|
|
240
|
+
# the swallow instead of a silent pass.
|
|
241
|
+
unwired_files_flagged: set[str] = set()
|
|
242
|
+
liveness_baseline_status: str | None = None
|
|
243
|
+
try:
|
|
244
|
+
from devcouncil.verification.checks.wiring import detect_unwired_file_gaps
|
|
245
|
+
|
|
246
|
+
unwired_gaps = detect_unwired_file_gaps(
|
|
247
|
+
task=task,
|
|
248
|
+
project_root=verifier.project_root,
|
|
249
|
+
changed_files=changed_files,
|
|
250
|
+
diff_content=diff_content,
|
|
251
|
+
get_untracked_files=verifier._get_untracked_files,
|
|
252
|
+
next_gap_id=verifier._next_gap_id,
|
|
253
|
+
unwired_enabled=rigor.unwired_enabled,
|
|
254
|
+
unwired_blocking=rigor.unwired_blocking,
|
|
255
|
+
classify_fn=verifier._classify_change_paths,
|
|
256
|
+
)
|
|
257
|
+
for g in unwired_gaps:
|
|
258
|
+
if g.file and g.gap_type == "unwired_file":
|
|
259
|
+
unwired_files_flagged.add(g.file)
|
|
260
|
+
gaps.extend(unwired_gaps)
|
|
261
|
+
except Exception:
|
|
262
|
+
logger.warning("unwired-file gate failed; skipping", exc_info=True)
|
|
263
|
+
if rigor.unwired_blocking:
|
|
264
|
+
gaps.append(Gap(
|
|
265
|
+
id=verifier._next_gap_id(task.id, "QGFAIL-unwired_file"),
|
|
266
|
+
severity="medium",
|
|
267
|
+
gap_type="quality_gate_failed",
|
|
268
|
+
task_id=task.id,
|
|
269
|
+
description="Unwired-file verification gate crashed; results are incomplete.",
|
|
270
|
+
evidence=["gate:unwired_file"],
|
|
271
|
+
recommended_fix="Re-run verify after fixing the gate error; do not treat this pass as proven.",
|
|
272
|
+
blocking=False,
|
|
273
|
+
))
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
from devcouncil.verification.checks.dead_symbols import detect_dead_symbol_gaps
|
|
277
|
+
|
|
278
|
+
gaps.extend(detect_dead_symbol_gaps(
|
|
279
|
+
task=task,
|
|
280
|
+
project_root=verifier.project_root,
|
|
281
|
+
diff_content=diff_content,
|
|
282
|
+
next_gap_id=verifier._next_gap_id,
|
|
283
|
+
dead_symbol_enabled=rigor.dead_symbol_enabled,
|
|
284
|
+
dead_symbol_blocking=rigor.dead_symbol_blocking,
|
|
285
|
+
requirements=requirements,
|
|
286
|
+
unwired_files=unwired_files_flagged,
|
|
287
|
+
))
|
|
288
|
+
except Exception:
|
|
289
|
+
logger.warning("dead-symbol gate failed; skipping", exc_info=True)
|
|
290
|
+
if rigor.dead_symbol_blocking:
|
|
291
|
+
gaps.append(Gap(
|
|
292
|
+
id=verifier._next_gap_id(task.id, "QGFAIL-dead_symbol"),
|
|
293
|
+
severity="medium",
|
|
294
|
+
gap_type="quality_gate_failed",
|
|
295
|
+
task_id=task.id,
|
|
296
|
+
description="Dead-symbol verification gate crashed; results are incomplete.",
|
|
297
|
+
evidence=["gate:dead_symbol"],
|
|
298
|
+
recommended_fix="Re-run verify after fixing the gate error; do not treat this pass as proven.",
|
|
299
|
+
blocking=False,
|
|
300
|
+
))
|
|
301
|
+
|
|
302
|
+
# Liveness ratchet: existing code newly stranded vs checkout baseline.
|
|
303
|
+
if rigor.liveness_ratchet_enabled:
|
|
304
|
+
try:
|
|
305
|
+
from devcouncil.indexing.repo_mapper import RepoMapper
|
|
306
|
+
from devcouncil.verification.checks.liveness_ratchet import (
|
|
307
|
+
detect_liveness_regressions,
|
|
308
|
+
load_liveness_baseline,
|
|
309
|
+
)
|
|
310
|
+
from devcouncil.verification.checks.wiring import collect_added_files
|
|
311
|
+
from devcouncil.verification.stub_detector import added_lines_by_file
|
|
312
|
+
|
|
313
|
+
baseline = load_liveness_baseline(verifier.project_root, task.id)
|
|
314
|
+
if baseline is None:
|
|
315
|
+
liveness_baseline_status = "missing"
|
|
316
|
+
else:
|
|
317
|
+
liveness_baseline_status = "ok"
|
|
318
|
+
mapper = RepoMapper(verifier.project_root)
|
|
319
|
+
current = mapper.liveness_snapshot()
|
|
320
|
+
added = collect_added_files(
|
|
321
|
+
project_root=verifier.project_root,
|
|
322
|
+
changed_files=changed_files,
|
|
323
|
+
diff_content=diff_content,
|
|
324
|
+
get_untracked_files=verifier._get_untracked_files,
|
|
325
|
+
classify_fn=verifier._classify_change_paths,
|
|
326
|
+
)
|
|
327
|
+
by_file = added_lines_by_file(diff_content or "")
|
|
328
|
+
diff_lines = {
|
|
329
|
+
path: {ln for ln, _ in lines}
|
|
330
|
+
for path, lines in by_file.items()
|
|
331
|
+
}
|
|
332
|
+
gaps.extend(detect_liveness_regressions(
|
|
333
|
+
baseline,
|
|
334
|
+
current,
|
|
335
|
+
added,
|
|
336
|
+
task=task,
|
|
337
|
+
next_gap_id=verifier._next_gap_id,
|
|
338
|
+
blocking=rigor.liveness_ratchet_blocking,
|
|
339
|
+
diff_added_lines=diff_lines,
|
|
340
|
+
))
|
|
341
|
+
except Exception:
|
|
342
|
+
logger.warning("liveness-ratchet gate failed; skipping", exc_info=True)
|
|
343
|
+
if rigor.liveness_ratchet_blocking:
|
|
344
|
+
gaps.append(Gap(
|
|
345
|
+
id=verifier._next_gap_id(task.id, "QGFAIL-liveness_ratchet"),
|
|
346
|
+
severity="medium",
|
|
347
|
+
gap_type="quality_gate_failed",
|
|
348
|
+
task_id=task.id,
|
|
349
|
+
description="Liveness-ratchet verification gate crashed; results are incomplete.",
|
|
350
|
+
evidence=["gate:liveness_ratchet"],
|
|
351
|
+
recommended_fix="Re-run verify after fixing the gate error; do not treat this pass as proven.",
|
|
352
|
+
blocking=False,
|
|
353
|
+
))
|
|
354
|
+
|
|
355
|
+
if rigor.effort_enabled and diff_content:
|
|
356
|
+
for eff in detect_effort_anomalies(
|
|
357
|
+
task, diff_content, requirements,
|
|
358
|
+
min_added_lines_per_planned_file=rigor.min_added_lines_per_planned_file,
|
|
359
|
+
):
|
|
360
|
+
gaps.append(Gap(
|
|
361
|
+
id=verifier._next_gap_id(task.id, f"EFFORT-{eff.file or eff.reason[:40]}"),
|
|
362
|
+
severity=cast(Literal["low", "medium", "high", "critical"], eff.severity),
|
|
363
|
+
gap_type="suspicious_effort",
|
|
364
|
+
task_id=task.id,
|
|
365
|
+
description=eff.detail,
|
|
366
|
+
evidence=[eff.reason],
|
|
367
|
+
recommended_fix=(
|
|
368
|
+
"Review whether the implementation actually fulfills the task scope; "
|
|
369
|
+
"complete the planned work (or restore removed tests), then re-verify."
|
|
370
|
+
),
|
|
371
|
+
blocking=rigor.effort_blocking,
|
|
372
|
+
file=eff.file,
|
|
373
|
+
))
|
|
374
|
+
|
|
375
|
+
compiler_active = bool(verifier.acceptance_compiler and diff_content and task.acceptance_criterion_ids)
|
|
376
|
+
log_step(
|
|
377
|
+
"verify/5: stub and effort heuristics",
|
|
378
|
+
project_root=verifier.project_root,
|
|
379
|
+
task_id=task.id,
|
|
380
|
+
compiler_active=compiler_active,
|
|
381
|
+
gaps_so_far=len(gaps),
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
cmd_run = run_verification_commands(
|
|
385
|
+
task=task,
|
|
386
|
+
commands_for_task=verifier._commands_for_task(task),
|
|
387
|
+
compiler_active=compiler_active,
|
|
388
|
+
command_applicable=verifier._command_applicable,
|
|
389
|
+
run_command=lambda cmd: verifier._run_command(cmd, task_id=task.id),
|
|
390
|
+
command_can_prove_acceptance=verifier._command_can_prove_acceptance,
|
|
391
|
+
command_is_malformed=verifier._command_is_malformed,
|
|
392
|
+
failure_location=verifier._failure_location,
|
|
393
|
+
is_quality_only_command=verifier._is_quality_only_command,
|
|
394
|
+
next_gap_id=verifier._next_gap_id,
|
|
395
|
+
retry_flaky=bool(_cfg.verification.retry_flaky) if _cfg is not None else True,
|
|
396
|
+
)
|
|
397
|
+
gaps.extend(cmd_run.gaps)
|
|
398
|
+
command_results: list[CommandResult] = list(cmd_run.command_results)
|
|
399
|
+
evidence_results: list[CommandResult] = list(cmd_run.evidence_results)
|
|
400
|
+
genuine_failure = cmd_run.genuine_failure
|
|
401
|
+
had_unrunnable = cmd_run.had_unrunnable
|
|
402
|
+
demoted_failures: list[Gap] = list(cmd_run.demoted_failures)
|
|
403
|
+
evidence_to_save.extend(command_results)
|
|
404
|
+
|
|
405
|
+
log_step(
|
|
406
|
+
"verify/6: verification commands",
|
|
407
|
+
project_root=verifier.project_root,
|
|
408
|
+
task_id=task.id,
|
|
409
|
+
commands_run=len(command_results),
|
|
410
|
+
gaps_so_far=len(gaps),
|
|
411
|
+
)
|
|
412
|
+
_repair_fn = getattr(verifier.acceptance_compiler, "repair", None)
|
|
413
|
+
compiled_run = await run_compiled_acceptance_checks(
|
|
414
|
+
task=task,
|
|
415
|
+
requirements=requirements,
|
|
416
|
+
compile_future=compile_future,
|
|
417
|
+
diff_content=diff_content,
|
|
418
|
+
ac_repair_attempts=ac_repair_attempts,
|
|
419
|
+
acceptance_compiler=verifier.acceptance_compiler,
|
|
420
|
+
command_applicable=verifier._command_applicable,
|
|
421
|
+
run_command=lambda cmd: verifier._run_command(cmd, task_id=task.id),
|
|
422
|
+
command_is_malformed=verifier._command_is_malformed,
|
|
423
|
+
failure_location=verifier._failure_location,
|
|
424
|
+
next_gap_id=verifier._next_gap_id,
|
|
425
|
+
repair_command=_repair_fn,
|
|
426
|
+
had_unrunnable=had_unrunnable,
|
|
427
|
+
genuine_failure=genuine_failure,
|
|
428
|
+
)
|
|
429
|
+
gaps.extend(compiled_run.gaps)
|
|
430
|
+
command_results.extend(compiled_run.command_results)
|
|
431
|
+
evidence_to_save.extend(compiled_run.evidence)
|
|
432
|
+
had_unrunnable = compiled_run.had_unrunnable
|
|
433
|
+
genuine_failure = compiled_run.genuine_failure
|
|
434
|
+
compiled_pass = compiled_run.compiled_pass
|
|
435
|
+
compiled_cmds_by_ac = compiled_run.compiled_cmds_by_ac
|
|
436
|
+
failing_results_by_ac = compiled_run.failing_results_by_ac
|
|
437
|
+
compiled_vote = compiled_run.compiled_vote
|
|
438
|
+
inconclusive_acs = compiled_run.inconclusive_acs
|
|
439
|
+
genuine_failure = promote_demoted_failures_when_compiler_incomplete(
|
|
440
|
+
task=task,
|
|
441
|
+
compiler_active=compiler_active,
|
|
442
|
+
compiled_cmds_by_ac=compiled_cmds_by_ac,
|
|
443
|
+
demoted_failures=demoted_failures,
|
|
444
|
+
genuine_failure=genuine_failure,
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
log_step(
|
|
448
|
+
"verify/7: compiled acceptance checks",
|
|
449
|
+
project_root=verifier.project_root,
|
|
450
|
+
task_id=task.id,
|
|
451
|
+
compiled_criteria=len(compiled_pass),
|
|
452
|
+
gaps_so_far=len(gaps),
|
|
453
|
+
)
|
|
454
|
+
ac_evidence = map_acceptance_criteria_evidence(
|
|
455
|
+
task=task,
|
|
456
|
+
requirements=requirements,
|
|
457
|
+
evidence_results=evidence_results,
|
|
458
|
+
compiled_pass=compiled_pass,
|
|
459
|
+
compiled_cmds_by_ac=compiled_cmds_by_ac,
|
|
460
|
+
failing_results_by_ac=failing_results_by_ac,
|
|
461
|
+
compiled_vote=compiled_vote,
|
|
462
|
+
inconclusive_acs=inconclusive_acs,
|
|
463
|
+
work_present=work_present,
|
|
464
|
+
genuine_failure=genuine_failure,
|
|
465
|
+
had_unrunnable=had_unrunnable,
|
|
466
|
+
rigor=rigor,
|
|
467
|
+
is_quality_only_command=verifier._is_quality_only_command,
|
|
468
|
+
next_gap_id=verifier._next_gap_id,
|
|
469
|
+
)
|
|
470
|
+
gaps.extend(ac_evidence.gaps)
|
|
471
|
+
evidence_to_save.extend(ac_evidence.evidence)
|
|
472
|
+
|
|
473
|
+
log_step(
|
|
474
|
+
"verify/8: acceptance-criteria evidence mapping",
|
|
475
|
+
project_root=verifier.project_root,
|
|
476
|
+
task_id=task.id,
|
|
477
|
+
gaps_so_far=len(gaps),
|
|
478
|
+
)
|
|
479
|
+
measure_cov, enforce_cov, min_ratio = verifier._diff_coverage_settings()
|
|
480
|
+
if rigor.enforce_coverage:
|
|
481
|
+
enforce_cov = True
|
|
482
|
+
any_passing = any(
|
|
483
|
+
result.exit_code == 0 and not verifier._is_quality_only_command(result.command)
|
|
484
|
+
for result in evidence_results
|
|
485
|
+
) or any(compiled_pass.values())
|
|
486
|
+
dc_result = run_diff_coverage_gate(
|
|
487
|
+
task=task,
|
|
488
|
+
diff_content=diff_content,
|
|
489
|
+
any_passing=any_passing,
|
|
490
|
+
enforce_cov=enforce_cov,
|
|
491
|
+
measure_cov=measure_cov,
|
|
492
|
+
min_ratio=min_ratio,
|
|
493
|
+
measure_diff_coverage=verifier.measure_diff_coverage,
|
|
494
|
+
coverage_target_commands=verifier._coverage_target_commands,
|
|
495
|
+
next_gap_id=verifier._next_gap_id,
|
|
496
|
+
)
|
|
497
|
+
gaps.extend(dc_result.gaps)
|
|
498
|
+
evidence_to_save.extend(dc_result.evidence)
|
|
499
|
+
coverage_measured = dc_result.coverage_measured
|
|
500
|
+
coverage_skipped_reason = dc_result.coverage_skipped_reason
|
|
501
|
+
|
|
502
|
+
log_step(
|
|
503
|
+
"verify/9: diff coverage",
|
|
504
|
+
project_root=verifier.project_root,
|
|
505
|
+
task_id=task.id,
|
|
506
|
+
coverage_measured=coverage_measured,
|
|
507
|
+
gaps_so_far=len(gaps),
|
|
508
|
+
)
|
|
509
|
+
if diff_content:
|
|
510
|
+
gaps.extend(verifier.secret_scanner.scan_diff(diff_content, task.id))
|
|
511
|
+
|
|
512
|
+
log_step(
|
|
513
|
+
"verify/10: secret scan",
|
|
514
|
+
project_root=verifier.project_root,
|
|
515
|
+
task_id=task.id,
|
|
516
|
+
gaps_so_far=len(gaps),
|
|
517
|
+
)
|
|
518
|
+
if review_future is not None:
|
|
519
|
+
try:
|
|
520
|
+
review_result = await review_future
|
|
521
|
+
for finding in review_result.findings:
|
|
522
|
+
review_key = (
|
|
523
|
+
f"{getattr(finding, 'file', '') or ''}:"
|
|
524
|
+
f"{getattr(finding, 'line', '') or ''}:"
|
|
525
|
+
f"{finding.description[:48]}"
|
|
526
|
+
)
|
|
527
|
+
finding.id = verifier._next_gap_id(task.id, f"REVIEW-{review_key}")
|
|
528
|
+
finding.blocking = bool(
|
|
529
|
+
rigor.reviewer_required and finding.severity == "critical"
|
|
530
|
+
)
|
|
531
|
+
gaps.append(finding)
|
|
532
|
+
except Exception as exc:
|
|
533
|
+
logger.error("Implementation review failed: %s", exc)
|
|
534
|
+
|
|
535
|
+
log_step(
|
|
536
|
+
"verify/11: implementation review",
|
|
537
|
+
project_root=verifier.project_root,
|
|
538
|
+
task_id=task.id,
|
|
539
|
+
gaps_so_far=len(gaps),
|
|
540
|
+
)
|
|
541
|
+
for card in unresolved_blocking_cards(verifier.project_root, task_id=task.id):
|
|
542
|
+
gaps.append(Gap(
|
|
543
|
+
id=verifier._next_gap_id(task.id, f"LIVE-{card.id}"),
|
|
544
|
+
severity="critical",
|
|
545
|
+
gap_type="architecture_drift",
|
|
546
|
+
task_id=task.id,
|
|
547
|
+
description=f"Open critical live-review card remains: {card.summary}",
|
|
548
|
+
evidence=[card.id, card.message_for_agent],
|
|
549
|
+
recommended_fix=(
|
|
550
|
+
f"Address the critique card, then run `dev watch resolve {card.id}` "
|
|
551
|
+
"or mark it ignored with justification outside the verification gate."
|
|
552
|
+
),
|
|
553
|
+
blocking=True,
|
|
554
|
+
))
|
|
555
|
+
|
|
556
|
+
# Post-step: a change spanning many subsystems / files likely stales the codebase
|
|
557
|
+
# wiki. Flag the stale pages (or auto-refresh, per config). Best-effort and never
|
|
558
|
+
# blocking — an orientation aid, not a gate.
|
|
559
|
+
wiki_refresh_outcome = None
|
|
560
|
+
try:
|
|
561
|
+
from devcouncil.verification.wiki_refresh import (
|
|
562
|
+
evaluate_wiki_refresh,
|
|
563
|
+
wiki_refresh_advisory_gap,
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
wiki_cfg = getattr(_cfg.verification, "wiki_refresh", None) if _cfg is not None else None
|
|
567
|
+
if changed_files and (wiki_cfg is None or wiki_cfg.enabled):
|
|
568
|
+
wiki_refresh_outcome = evaluate_wiki_refresh(
|
|
569
|
+
verifier.project_root,
|
|
570
|
+
changed_files,
|
|
571
|
+
repo_map=verifier._load_repo_map(),
|
|
572
|
+
config=wiki_cfg,
|
|
573
|
+
)
|
|
574
|
+
advisory = wiki_refresh_advisory_gap(
|
|
575
|
+
wiki_refresh_outcome,
|
|
576
|
+
task_id=task.id,
|
|
577
|
+
gap_id=verifier._next_gap_id(task.id, "WIKI"),
|
|
578
|
+
)
|
|
579
|
+
if advisory is not None:
|
|
580
|
+
gaps.append(advisory)
|
|
581
|
+
except Exception as exc:
|
|
582
|
+
logger.debug("wiki refresh post-step skipped: %s", exc)
|
|
583
|
+
|
|
584
|
+
from devcouncil.verification.gap_ids import normalize_verify_gaps
|
|
585
|
+
from devcouncil.verification.verifier import VerificationOutcome
|
|
586
|
+
|
|
587
|
+
gaps = normalize_verify_gaps(gaps)
|
|
588
|
+
blocking_count = len([g for g in gaps if g.blocking])
|
|
589
|
+
log_step(
|
|
590
|
+
"verify/complete",
|
|
591
|
+
project_root=verifier.project_root,
|
|
592
|
+
task_id=task.id,
|
|
593
|
+
total_gaps=len(gaps),
|
|
594
|
+
blocking_gaps=blocking_count,
|
|
595
|
+
evidence_items=len(evidence_to_save),
|
|
596
|
+
trace=True,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
verifier.last_outcome = VerificationOutcome(
|
|
600
|
+
mode=_verification_mode_label(evidence_to_save, compiler_active=compiler_active),
|
|
601
|
+
compiler_active=compiler_active,
|
|
602
|
+
diff_empty=diff_empty,
|
|
603
|
+
coverage_measured=coverage_measured,
|
|
604
|
+
coverage_skipped_reason=coverage_skipped_reason,
|
|
605
|
+
difficulty=rigor.difficulty,
|
|
606
|
+
rigor_applied=list(rigor.applied),
|
|
607
|
+
wiki_refresh=wiki_refresh_outcome.to_dict() if wiki_refresh_outcome is not None else None,
|
|
608
|
+
liveness_baseline=liveness_baseline_status,
|
|
609
|
+
)
|
|
610
|
+
return gaps, evidence_to_save
|