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
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import hashlib
|
|
3
3
|
import logging
|
|
4
|
-
import subprocess
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from types import SimpleNamespace
|
|
7
6
|
|
|
@@ -27,6 +26,7 @@ from devcouncil.integrations.github_intent import resolve_goal_intent
|
|
|
27
26
|
from devcouncil.llm.provider import ProviderRequestError
|
|
28
27
|
from devcouncil.llm.router import StructuredOutputError
|
|
29
28
|
from devcouncil.storage.db import get_db
|
|
29
|
+
from devcouncil.utils.proc import run_git
|
|
30
30
|
from devcouncil.storage.repositories import ArtifactGraphRepository, GapRepository, StateRepository, TaskRepository
|
|
31
31
|
from devcouncil.app.state_machine import ProjectPhase
|
|
32
32
|
from devcouncil.gating.policy import topological_order
|
|
@@ -63,10 +63,7 @@ def _is_git_repo(root: Path) -> bool:
|
|
|
63
63
|
non-git tree would spuriously flag unrelated files as orphan diffs.
|
|
64
64
|
"""
|
|
65
65
|
try:
|
|
66
|
-
result =
|
|
67
|
-
["git", "rev-parse", "--is-inside-work-tree"],
|
|
68
|
-
cwd=root, capture_output=True, text=True,
|
|
69
|
-
)
|
|
66
|
+
result = run_git(["rev-parse", "--is-inside-work-tree"], cwd=root)
|
|
70
67
|
except Exception:
|
|
71
68
|
return False
|
|
72
69
|
return result.returncode == 0 and result.stdout.strip() == "true"
|
|
@@ -98,6 +95,22 @@ def _max_repair_attempts(root: Path) -> int:
|
|
|
98
95
|
return 0
|
|
99
96
|
|
|
100
97
|
|
|
98
|
+
def _task_max_repairs(root: Path, task, base_max: int) -> int:
|
|
99
|
+
"""Per-task repair budget: the base budget widened for HARD tasks per
|
|
100
|
+
``verification.rigor.extra_repair_attempts_on_hard``. A disabled loop
|
|
101
|
+
(base 0) stays disabled. Never raises."""
|
|
102
|
+
if base_max <= 0:
|
|
103
|
+
return base_max
|
|
104
|
+
try:
|
|
105
|
+
from devcouncil.verification.difficulty import resolve_rigor_policy
|
|
106
|
+
|
|
107
|
+
config = load_config(root)
|
|
108
|
+
policy = resolve_rigor_policy(task, None, config=config)
|
|
109
|
+
return base_max + max(0, policy.extra_repair_attempts)
|
|
110
|
+
except Exception:
|
|
111
|
+
return base_max
|
|
112
|
+
|
|
113
|
+
|
|
101
114
|
def _blocking_gap_signature(root: Path, task_id: str) -> str:
|
|
102
115
|
"""Fingerprint of a task's current blocking gaps, for no-progress detection.
|
|
103
116
|
|
|
@@ -130,9 +143,11 @@ def _remediable_incomplete_signature(root: Path, task_id: str) -> str:
|
|
|
130
143
|
|
|
131
144
|
|
|
132
145
|
def _build_repair_service(root: Path):
|
|
133
|
-
"""
|
|
134
|
-
|
|
135
|
-
|
|
146
|
+
"""Optional LLM advisory layer for sharpening correction-manifest root cause.
|
|
147
|
+
|
|
148
|
+
Returns None when no provider key is configured — the Stable repair contract
|
|
149
|
+
does not require this: ``write_correction_manifest`` still builds a deterministic,
|
|
150
|
+
task-scoped manifest from blocking gaps, next-actions, and task scope."""
|
|
136
151
|
try:
|
|
137
152
|
from devcouncil.app.config import get_api_key
|
|
138
153
|
from devcouncil.llm.provider import create_provider, validate_model_provider
|
|
@@ -149,6 +164,61 @@ def _build_repair_service(root: Path):
|
|
|
149
164
|
return None
|
|
150
165
|
|
|
151
166
|
|
|
167
|
+
def _executor_run_failed(root: Path, task_id: str) -> bool:
|
|
168
|
+
"""True when the latest coding-agent run for ``task_id`` exited non-zero."""
|
|
169
|
+
from devcouncil.planning.correction_manifest import _latest_agent_run
|
|
170
|
+
|
|
171
|
+
run = _latest_agent_run(root, task_id)
|
|
172
|
+
if not run:
|
|
173
|
+
return False
|
|
174
|
+
returncode = run.get("returncode")
|
|
175
|
+
return returncode is not None and returncode != 0
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _executor_run_unavailable(root: Path, task_id: str) -> bool:
|
|
179
|
+
"""True when the agent failed for infra reasons (session/rate limit), not code defects."""
|
|
180
|
+
from devcouncil.executors.advisor_tool import ADVISOR_INFRA_FAILURE_MARKERS
|
|
181
|
+
from devcouncil.planning.correction_manifest import _latest_agent_run
|
|
182
|
+
|
|
183
|
+
run = _latest_agent_run(root, task_id)
|
|
184
|
+
if not run:
|
|
185
|
+
return False
|
|
186
|
+
returncode = run.get("returncode")
|
|
187
|
+
if returncode in (None, 0):
|
|
188
|
+
return False
|
|
189
|
+
parts: list[str] = [str(run.get("status") or "")]
|
|
190
|
+
for key in ("stderr_preview", "stdout_preview"):
|
|
191
|
+
val = run.get(key)
|
|
192
|
+
if isinstance(val, list):
|
|
193
|
+
parts.extend(str(x) for x in val)
|
|
194
|
+
elif val:
|
|
195
|
+
parts.append(str(val))
|
|
196
|
+
hay = " ".join(parts).lower()
|
|
197
|
+
needles = (
|
|
198
|
+
"session limit",
|
|
199
|
+
"rate limit",
|
|
200
|
+
"too many requests",
|
|
201
|
+
"limit_rpm",
|
|
202
|
+
"429",
|
|
203
|
+
*ADVISOR_INFRA_FAILURE_MARKERS,
|
|
204
|
+
)
|
|
205
|
+
return any(needle in hay for needle in needles)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _reverify_task(root: Path, task_id: str) -> str:
|
|
209
|
+
"""Re-run verification for one task without invoking the coding agent."""
|
|
210
|
+
db = get_db(root)
|
|
211
|
+
if not db:
|
|
212
|
+
return _task_status(root, task_id)
|
|
213
|
+
try:
|
|
214
|
+
verify_command._run_verify_body(root, task_id, "local", True, db)
|
|
215
|
+
except typer.Exit:
|
|
216
|
+
pass
|
|
217
|
+
except Exception as exc: # pragma: no cover - verify should not abort the loop
|
|
218
|
+
logger.warning("Verify-only pass for %s failed: %s", task_id, exc)
|
|
219
|
+
return _task_status(root, task_id)
|
|
220
|
+
|
|
221
|
+
|
|
152
222
|
def _execute_task_with_repair(
|
|
153
223
|
root: Path,
|
|
154
224
|
task,
|
|
@@ -163,12 +233,17 @@ def _execute_task_with_repair(
|
|
|
163
233
|
"""Run a task, then self-repair in a bounded loop until it verifies or the budget
|
|
164
234
|
is exhausted. Returns ``(final_status, repair_attempts_used)``.
|
|
165
235
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
236
|
+
**Stable contract:** each repair attempt writes a deterministic correction manifest
|
|
237
|
+
(blocking gaps, next-actions, task scope, prior-attempt context), re-runs the
|
|
238
|
+
executor, respects ``max_repairs``, and stops on no-progress (identical gap
|
|
239
|
+
fingerprint). Optional ``repair_service`` LLM inference may sharpen the manifest but
|
|
240
|
+
is not required for the loop to function.
|
|
241
|
+
|
|
242
|
+
Between attempts the prior work is committed so the readiness gate's clean-tree
|
|
243
|
+
requirement holds and the next attempt builds on it; verification still recognises
|
|
244
|
+
the committed work via the task's checkpoint. The loop stops early when an attempt
|
|
245
|
+
makes no progress (the same blocking gaps reappear) so it never spins on an
|
|
246
|
+
unfixable gate.
|
|
172
247
|
|
|
173
248
|
When the task ultimately verifies, the intermediate ``[blocked]`` commits made
|
|
174
249
|
between attempts are squashed into a single verified commit (see
|
|
@@ -197,6 +272,13 @@ def _execute_task_with_repair(
|
|
|
197
272
|
status = _task_status(root, task.id)
|
|
198
273
|
logger.info("Initial run of %s finished as %s (max_repairs=%d)", task.id, status, max_repairs)
|
|
199
274
|
|
|
275
|
+
if status in {"verified", "done"} and _remediable_incomplete_signature(root, task.id):
|
|
276
|
+
console.print(
|
|
277
|
+
f"[dim]{task.id}: re-verifying (incomplete acceptance criteria only)...[/dim]"
|
|
278
|
+
)
|
|
279
|
+
status = _reverify_task(root, task.id)
|
|
280
|
+
logger.info("After initial verify-only pass, %s is %s", task.id, status)
|
|
281
|
+
|
|
200
282
|
attempt = 0
|
|
201
283
|
last_signature: str | None = None
|
|
202
284
|
while attempt < max_repairs:
|
|
@@ -214,13 +296,68 @@ def _execute_task_with_repair(
|
|
|
214
296
|
# (e.g. the executor failed to start).
|
|
215
297
|
break
|
|
216
298
|
if signature == last_signature:
|
|
217
|
-
|
|
299
|
+
if _executor_run_unavailable(root, task.id):
|
|
300
|
+
# Advisor/session/rate-limit failures are not code gaps — do not burn
|
|
301
|
+
# the remaining repair budget on identical executor invocations.
|
|
302
|
+
logger.warning(
|
|
303
|
+
"%s: executor unavailable (infra); stopping self-repair loop",
|
|
304
|
+
task.id,
|
|
305
|
+
)
|
|
306
|
+
console.print(
|
|
307
|
+
f"[yellow]{task.id}: executor unavailable (infra failure); "
|
|
308
|
+
"stopping the self-repair loop.[/yellow]"
|
|
309
|
+
)
|
|
310
|
+
break
|
|
311
|
+
if _executor_run_failed(root, task.id):
|
|
312
|
+
logger.warning(
|
|
313
|
+
"%s: identical gaps but the last executor run failed to complete; "
|
|
314
|
+
"continuing the repair budget (infra failure is not no-progress)",
|
|
315
|
+
task.id,
|
|
316
|
+
)
|
|
317
|
+
console.print(
|
|
318
|
+
f"[yellow]{task.id}: executor failed on the last repair attempt; "
|
|
319
|
+
"retrying while repair budget remains.[/yellow]"
|
|
320
|
+
)
|
|
321
|
+
else:
|
|
322
|
+
logger.warning("%s: repair made no progress (identical gaps) after attempt %d; stopping loop", task.id, attempt)
|
|
323
|
+
console.print(
|
|
324
|
+
f"[yellow]{task.id}: repair made no progress (identical blocking gaps); "
|
|
325
|
+
"stopping the self-repair loop.[/yellow]"
|
|
326
|
+
)
|
|
327
|
+
break
|
|
328
|
+
last_signature = signature
|
|
329
|
+
|
|
330
|
+
incomplete_only = not blocked and bool(signature)
|
|
331
|
+
unavailable = _executor_run_unavailable(root, task.id)
|
|
332
|
+
# Verify-only when incomplete-only OR any infra unavailability (blocked or not).
|
|
333
|
+
if incomplete_only or unavailable:
|
|
218
334
|
console.print(
|
|
219
|
-
f"[
|
|
220
|
-
"
|
|
335
|
+
f"[dim]{task.id}: verify-only pass before repair "
|
|
336
|
+
f"(attempt {attempt + 1}/{max_repairs})...[/dim]"
|
|
221
337
|
)
|
|
222
|
-
|
|
223
|
-
|
|
338
|
+
prev_sig = signature
|
|
339
|
+
status = _reverify_task(root, task.id)
|
|
340
|
+
blocked = status not in {"verified", "done"}
|
|
341
|
+
new_sig = (
|
|
342
|
+
_blocking_gap_signature(root, task.id) if blocked
|
|
343
|
+
else _remediable_incomplete_signature(root, task.id)
|
|
344
|
+
)
|
|
345
|
+
if not new_sig:
|
|
346
|
+
break
|
|
347
|
+
if new_sig != prev_sig:
|
|
348
|
+
last_signature = None
|
|
349
|
+
continue
|
|
350
|
+
if unavailable:
|
|
351
|
+
logger.warning(
|
|
352
|
+
"%s: executor unavailable and verify-only did not clear gaps; "
|
|
353
|
+
"skipping further executor invocations",
|
|
354
|
+
task.id,
|
|
355
|
+
)
|
|
356
|
+
console.print(
|
|
357
|
+
f"[yellow]{task.id}: executor unavailable; gaps remain after "
|
|
358
|
+
"verify-only — not burning more repair budget.[/yellow]"
|
|
359
|
+
)
|
|
360
|
+
break
|
|
224
361
|
|
|
225
362
|
# Record where history started before the first failed-attempt commit, so the
|
|
226
363
|
# squash collapses exactly this task's intermediate commits and nothing earlier.
|
|
@@ -239,14 +376,36 @@ def _execute_task_with_repair(
|
|
|
239
376
|
if manifest_path is None:
|
|
240
377
|
break
|
|
241
378
|
attempt += 1
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
379
|
+
with log_stage(
|
|
380
|
+
"repair",
|
|
381
|
+
project_root=root,
|
|
382
|
+
task_id=task.id,
|
|
383
|
+
attempt=attempt,
|
|
384
|
+
max_repairs=max_repairs,
|
|
385
|
+
):
|
|
386
|
+
log_step(
|
|
387
|
+
f"repair attempt {attempt}/{max_repairs} for {task.id}",
|
|
388
|
+
project_root=root,
|
|
389
|
+
task_id=task.id,
|
|
390
|
+
manifest=str(manifest_path),
|
|
391
|
+
prior_status=status,
|
|
392
|
+
trace=True,
|
|
393
|
+
)
|
|
394
|
+
logger.info("Self-repair attempt %d/%d for %s (was %s); manifest=%s", attempt, max_repairs, task.id, status, manifest_path)
|
|
395
|
+
console.print(
|
|
396
|
+
f"\n[bold]Self-repair attempt {attempt}/{max_repairs}[/bold] for "
|
|
397
|
+
f"[bold]{task.id}[/bold] (was {status})..."
|
|
398
|
+
)
|
|
399
|
+
_run_once()
|
|
400
|
+
status = _task_status(root, task.id)
|
|
401
|
+
logger.info("After repair attempt %d, %s is now %s", attempt, task.id, status)
|
|
402
|
+
log_step(
|
|
403
|
+
f"repair attempt {attempt}/{max_repairs} finished for {task.id}",
|
|
404
|
+
project_root=root,
|
|
405
|
+
task_id=task.id,
|
|
406
|
+
status=status,
|
|
407
|
+
trace=True,
|
|
408
|
+
)
|
|
250
409
|
|
|
251
410
|
if status not in {"verified", "done"} and attempt >= max_repairs and max_repairs > 0:
|
|
252
411
|
logger.warning("%s: gave up after %d repair attempt(s); still %s", task.id, attempt, status)
|
|
@@ -254,6 +413,11 @@ def _execute_task_with_repair(
|
|
|
254
413
|
f"[yellow]{task.id}: gave up after {attempt} repair attempt(s); still {status}.[/yellow]"
|
|
255
414
|
)
|
|
256
415
|
|
|
416
|
+
if _remediable_incomplete_signature(root, task.id):
|
|
417
|
+
console.print(f"[dim]{task.id}: final verify-only pass for remaining incomplete gaps...[/dim]")
|
|
418
|
+
status = _reverify_task(root, task.id)
|
|
419
|
+
logger.info("After final verify-only pass, %s is %s", task.id, status)
|
|
420
|
+
|
|
257
421
|
# On success, squash the [blocked] attempt commits into one verified commit so the
|
|
258
422
|
# user's history isn't littered with failed attempts. Only do this when the task
|
|
259
423
|
# actually verified — a still-blocked task keeps its attempt commits so the work
|
|
@@ -272,10 +436,7 @@ def _execute_task_with_repair(
|
|
|
272
436
|
def _current_head(root: Path) -> str | None:
|
|
273
437
|
"""Resolve the current HEAD commit, or None when there is no commit / no git."""
|
|
274
438
|
try:
|
|
275
|
-
result =
|
|
276
|
-
["git", "rev-parse", "HEAD"],
|
|
277
|
-
cwd=root, capture_output=True, text=True,
|
|
278
|
-
)
|
|
439
|
+
result = run_git(["rev-parse", "HEAD"], cwd=root)
|
|
279
440
|
except Exception:
|
|
280
441
|
return None
|
|
281
442
|
if result.returncode != 0:
|
|
@@ -305,41 +466,31 @@ def _squash_repair_commits(root: Path, task_id: str, base: str, status: str) ->
|
|
|
305
466
|
head = _current_head(root)
|
|
306
467
|
if not head or head == base:
|
|
307
468
|
return False
|
|
308
|
-
base_ok =
|
|
309
|
-
["git", "rev-parse", "--verify", f"{base}^{{commit}}"],
|
|
310
|
-
cwd=root, capture_output=True, text=True,
|
|
311
|
-
)
|
|
469
|
+
base_ok = run_git(["rev-parse", "--verify", f"{base}^{{commit}}"], cwd=root)
|
|
312
470
|
if base_ok.returncode != 0:
|
|
313
471
|
return False
|
|
314
472
|
# Soft reset keeps the working tree + index exactly as-is; only the branch
|
|
315
473
|
# pointer moves back to base, so the next commit captures the whole task.
|
|
316
|
-
reset =
|
|
317
|
-
["git", "reset", "--soft", base],
|
|
318
|
-
cwd=root, capture_output=True, text=True,
|
|
319
|
-
)
|
|
474
|
+
reset = run_git(["reset", "--soft", base], cwd=root)
|
|
320
475
|
if reset.returncode != 0:
|
|
321
476
|
return False
|
|
322
477
|
# Stage the FINAL (verified) attempt's still-uncommitted changes too, so they land
|
|
323
478
|
# in this single squash commit. Without this they'd be committed separately by the
|
|
324
479
|
# caller afterward, producing two commits for what the message calls "one verified
|
|
325
480
|
# commit" (and leaving the squash to capture only the [blocked] diffs).
|
|
326
|
-
add =
|
|
327
|
-
["git", "add", "-A"],
|
|
328
|
-
cwd=root, capture_output=True, text=True,
|
|
329
|
-
)
|
|
481
|
+
add = run_git(["add", "-A"], cwd=root)
|
|
330
482
|
if add.returncode != 0:
|
|
331
483
|
return False
|
|
332
484
|
# Re-commit the squashed tree. There may be nothing staged if every attempt's
|
|
333
485
|
# changes cancelled out (unlikely for a verified task) — tolerate that.
|
|
334
|
-
commit =
|
|
486
|
+
commit = run_git(
|
|
335
487
|
[
|
|
336
|
-
"git",
|
|
337
488
|
"-c", "user.name=DevCouncil",
|
|
338
489
|
"-c", "user.email=devcouncil@local",
|
|
339
490
|
"commit", "--no-verify", "--allow-empty",
|
|
340
491
|
"-m", f"devcouncil(e2e): {task_id} [{status}]",
|
|
341
492
|
],
|
|
342
|
-
cwd=root,
|
|
493
|
+
cwd=root,
|
|
343
494
|
)
|
|
344
495
|
return commit.returncode == 0
|
|
345
496
|
except Exception:
|
|
@@ -356,22 +507,18 @@ def _commit_task_changes(root: Path, task_id: str, status: str) -> bool:
|
|
|
356
507
|
and can be squashed or reset afterwards. Returns True if a commit was made.
|
|
357
508
|
"""
|
|
358
509
|
try:
|
|
359
|
-
status_out =
|
|
360
|
-
["git", "status", "--porcelain"],
|
|
361
|
-
cwd=root, capture_output=True, text=True,
|
|
362
|
-
)
|
|
510
|
+
status_out = run_git(["status", "--porcelain"], cwd=root)
|
|
363
511
|
if status_out.returncode != 0 or not status_out.stdout.strip():
|
|
364
512
|
return False
|
|
365
|
-
|
|
366
|
-
commit =
|
|
513
|
+
run_git(["add", "-A"], cwd=root, check=True)
|
|
514
|
+
commit = run_git(
|
|
367
515
|
[
|
|
368
|
-
"git",
|
|
369
516
|
"-c", "user.name=DevCouncil",
|
|
370
517
|
"-c", "user.email=devcouncil@local",
|
|
371
518
|
"commit", "--no-verify",
|
|
372
519
|
"-m", f"devcouncil(e2e): {task_id} [{status}]",
|
|
373
520
|
],
|
|
374
|
-
cwd=root,
|
|
521
|
+
cwd=root,
|
|
375
522
|
)
|
|
376
523
|
return commit.returncode == 0
|
|
377
524
|
except Exception:
|
|
@@ -626,12 +773,14 @@ def go(
|
|
|
626
773
|
executed_task_ids.append(task.id)
|
|
627
774
|
# Run, then self-repair in a bounded loop (closes the autonomous loop: the
|
|
628
775
|
# one-shot executor no longer needs a human to run `dev repair` and re-run).
|
|
776
|
+
# Hard tasks get a wider budget per verification.rigor.
|
|
777
|
+
task_max_repairs = _task_max_repairs(root, task, max_repairs)
|
|
629
778
|
with log_stage(
|
|
630
779
|
"execute_task",
|
|
631
780
|
project_root=root,
|
|
632
781
|
task_id=task.id,
|
|
633
782
|
executor=normalized_executor,
|
|
634
|
-
max_repairs=
|
|
783
|
+
max_repairs=task_max_repairs,
|
|
635
784
|
):
|
|
636
785
|
latest_status, repairs_used = _execute_task_with_repair(
|
|
637
786
|
root,
|
|
@@ -639,7 +788,7 @@ def go(
|
|
|
639
788
|
executor=normalized_executor,
|
|
640
789
|
profile=profile,
|
|
641
790
|
stream=stream,
|
|
642
|
-
max_repairs=
|
|
791
|
+
max_repairs=task_max_repairs,
|
|
643
792
|
repair_service=repair_service,
|
|
644
793
|
config=repair_config,
|
|
645
794
|
)
|
|
@@ -685,17 +834,18 @@ def go(
|
|
|
685
834
|
# refreshes statuses/gaps cheaply for an honest final report.
|
|
686
835
|
if executed_task_ids and _is_git_repo(root):
|
|
687
836
|
console.print("\n[bold]Reconciling verification against the final integrated state...[/bold]")
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
837
|
+
with log_stage("reconcile", project_root=root, tasks=len(executed_task_ids)):
|
|
838
|
+
log_step("reconcile: re-verifying against integrated state", project_root=root)
|
|
839
|
+
try:
|
|
840
|
+
verify_command.verify(task_id=None, sandbox="local", json_format=True, project_root=root)
|
|
841
|
+
except typer.Exit:
|
|
842
|
+
# Expected signal: verify() raises Exit(code=1) when any task is blocked,
|
|
843
|
+
# but it has already persisted every task status before raising. The
|
|
844
|
+
# reconciliation pass therefore completed — fall through to the reload so
|
|
845
|
+
# blocked statuses are refreshed honestly. (Only real errors should skip.)
|
|
846
|
+
pass
|
|
847
|
+
except Exception as exc: # pragma: no cover - reconciliation is best-effort
|
|
848
|
+
console.print(f"[yellow]Reconciliation pass skipped: {exc}[/yellow]")
|
|
699
849
|
reconciled = {item.id: item for item in _load_tasks(root)}
|
|
700
850
|
# Rebuild from the FULL planned set, not just executed_task_ids: a task skipped
|
|
701
851
|
# for an unmet dependency, or one reconciliation downgraded from done->blocked,
|
|
@@ -715,7 +865,7 @@ def go(
|
|
|
715
865
|
logger.warning("dev go finished with %d unfinished task(s): %s", len(failed), ", ".join(failed))
|
|
716
866
|
_record_project_blocked(root)
|
|
717
867
|
|
|
718
|
-
log_step("generating final report", project_root=root)
|
|
868
|
+
log_step("generating final report", project_root=root, trace=True)
|
|
719
869
|
console.print("\n[bold]Final DevCouncil report[/bold]")
|
|
720
870
|
report_command.report(
|
|
721
871
|
SimpleNamespace(invoked_subcommand=None), # type: ignore[arg-type]
|
|
@@ -724,6 +874,8 @@ def go(
|
|
|
724
874
|
github=False,
|
|
725
875
|
github_pr_comment=False,
|
|
726
876
|
gitlab_pr_comment=False,
|
|
877
|
+
evidence_json=None,
|
|
878
|
+
fail_on_blocking=False,
|
|
727
879
|
project_root=root,
|
|
728
880
|
)
|
|
729
881
|
if report_file is not None:
|