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