core-runtime 0.1.1__py3-none-any.whl
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.
- api/__init__.py +4 -0
- api/cli/__init__.py +10 -0
- api/cli/audit_client.py +92 -0
- api/cli/census_client.py +77 -0
- api/cli/client.py +508 -0
- api/cli/coverage_client.py +121 -0
- api/cli/daemon_client.py +42 -0
- api/cli/fix_client.py +101 -0
- api/cli/inspect_client.py +143 -0
- api/cli/integration_client.py +42 -0
- api/cli/integrity_client.py +45 -0
- api/cli/proposals_client.py +101 -0
- api/cli/quality_client.py +83 -0
- api/cli/refactor_client.py +82 -0
- api/cli/sync_client.py +106 -0
- api/dependencies.py +37 -0
- api/errors.py +74 -0
- api/main.py +88 -0
- api/v1/__init__.py +4 -0
- api/v1/audit_routes.py +302 -0
- api/v1/census_routes.py +207 -0
- api/v1/coverage_routes.py +352 -0
- api/v1/daemon_routes.py +86 -0
- api/v1/development_routes.py +70 -0
- api/v1/fix_routes.py +428 -0
- api/v1/inspect_routes.py +220 -0
- api/v1/integration_routes.py +56 -0
- api/v1/integrity_routes.py +72 -0
- api/v1/knowledge_routes.py +36 -0
- api/v1/lint_routes.py +40 -0
- api/v1/proposals_routes.py +267 -0
- api/v1/quality_routes.py +272 -0
- api/v1/refactor_routes.py +225 -0
- api/v1/sync_routes.py +261 -0
- body/__init__.py +4 -0
- body/analyzers/__init__.py +25 -0
- body/analyzers/base_analyzer.py +28 -0
- body/analyzers/constitutional_path_analyzer.py +63 -0
- body/analyzers/file_analyzer.py +211 -0
- body/analyzers/knowledge_graph_analyzer.py +86 -0
- body/analyzers/prompt_analyzer.py +155 -0
- body/analyzers/symbol_extractor.py +280 -0
- body/atomic/__init__.py +74 -0
- body/atomic/build_tests_action.py +354 -0
- body/atomic/check_actions.py +142 -0
- body/atomic/crate_ops.py +59 -0
- body/atomic/executor.py +470 -0
- body/atomic/file_ops.py +153 -0
- body/atomic/fix_actions.py +1142 -0
- body/atomic/import_resolver.py +200 -0
- body/atomic/metadata_ops.py +161 -0
- body/atomic/modularity_fix.py +839 -0
- body/atomic/modularity_splitter.py +521 -0
- body/atomic/proposal_lifecycle_actions.py +171 -0
- body/atomic/registry.py +326 -0
- body/atomic/remediate_cognitive_role.py +104 -0
- body/atomic/sandbox_lifecycle.py +252 -0
- body/atomic/split_plan.py +155 -0
- body/atomic/sync_actions/__init__.py +22 -0
- body/atomic/sync_actions/chunking_helpers.py +218 -0
- body/atomic/sync_actions/sync_actions.py +431 -0
- body/atomic/test_actions.py +47 -0
- body/autonomy/__init__.py +16 -0
- body/autonomy/audit_analyzer.py +343 -0
- body/autonomy/micro_proposal_executor.py +316 -0
- body/crate_processing/canary_executor.py +144 -0
- body/evaluators/__init__.py +27 -0
- body/evaluators/atomic_actions_evaluator.py +162 -0
- body/evaluators/atomic_actions_format.py +45 -0
- body/evaluators/atomic_actions_rules.py +278 -0
- body/evaluators/base_evaluator.py +67 -0
- body/evaluators/clarity_evaluator.py +83 -0
- body/evaluators/constitutional_evaluator.py +227 -0
- body/evaluators/failure_evaluator.py +136 -0
- body/evaluators/performance_evaluator.py +341 -0
- body/evaluators/security_evaluator.py +393 -0
- body/flows/__init__.py +33 -0
- body/flows/executor.py +275 -0
- body/flows/registry.py +272 -0
- body/flows/result.py +113 -0
- body/governance/__init__.py +29 -0
- body/governance/body_contracts_service.py +317 -0
- body/governance/code_validator.py +118 -0
- body/governance/engine_dispatcher.py +128 -0
- body/governance/intent_guard.py +592 -0
- body/governance/intent_pattern_validators.py +197 -0
- body/governance/key_management_service.py +100 -0
- body/governance/path_validator.py +187 -0
- body/governance/remediation_service.py +263 -0
- body/governance/runtime_validator.py +184 -0
- body/infrastructure/bootstrap.py +135 -0
- body/infrastructure/lifespan.py +85 -0
- body/infrastructure/repositories/__init__.py +1 -0
- body/infrastructure/repositories/decision_trace_repository.py +210 -0
- body/infrastructure/repositories/refusal_repository.py +294 -0
- body/introspection/__init__.py +4 -0
- body/introspection/capability_discovery_service.py +107 -0
- body/introspection/discovery/from_kgb.py +33 -0
- body/introspection/discovery/from_source_scan.py +40 -0
- body/introspection/discovery/loader.py +62 -0
- body/introspection/discovery/registry.py +22 -0
- body/introspection/discovery/semantics.py +21 -0
- body/introspection/discovery/sync.py +47 -0
- body/introspection/drift_detector.py +81 -0
- body/introspection/drift_service.py +48 -0
- body/introspection/export_vectors.py +133 -0
- body/introspection/generate_capability_docs.py +130 -0
- body/introspection/generate_correction_map.py +98 -0
- body/introspection/graph_analysis_service.py +66 -0
- body/introspection/knowledge_graph_service.py +170 -0
- body/introspection/semantic_clusterer.py +128 -0
- body/introspection/symbol_index_builder.py +244 -0
- body/introspection/sync/engine.py +92 -0
- body/introspection/sync/scanner.py +56 -0
- body/introspection/sync/visitor.py +83 -0
- body/introspection/sync_service.py +62 -0
- body/invokers/__init__.py +4 -0
- body/invokers/capability_invoker.py +4 -0
- body/maintenance/command_sync_service.py +73 -0
- body/maintenance/dotenv_sync_service.py +99 -0
- body/maintenance/idempotency_harness.py +70 -0
- body/maintenance/maintenance_service.py +123 -0
- body/maintenance/memory_cleanup_service.py +131 -0
- body/maintenance/refactor_settings_access.py +274 -0
- body/maintenance/scripts/__init__.py +5 -0
- body/maintenance/scripts/context_export.py +278 -0
- body/maintenance/scripts/vector_verify.py +73 -0
- body/maintenance/sync_vectors.py +338 -0
- body/models/__init__.py +4 -0
- body/project_lifecycle/__init__.py +4 -0
- body/project_lifecycle/bootstrap_service.py +157 -0
- body/project_lifecycle/definition_service.py +264 -0
- body/project_lifecycle/integration_service.py +113 -0
- body/project_lifecycle/scaffolding_service.py +187 -0
- body/quality/coverage_analyzer.py +177 -0
- body/repositories/__init__.py +4 -0
- body/self_healing/atomic_actions_fixer.py +113 -0
- body/self_healing/body_ui_fixer.py +193 -0
- body/self_healing/capability_parser.py +65 -0
- body/self_healing/code_style_service.py +51 -0
- body/self_healing/complexity_filter.py +70 -0
- body/self_healing/docstring_service.py +345 -0
- body/self_healing/duplicate_id_service.py +407 -0
- body/self_healing/duplicates_service.py +165 -0
- body/self_healing/handlers/__init__.py +1 -0
- body/self_healing/handlers/id_assignment_handler.py +201 -0
- body/self_healing/handlers/import_sorting_handler.py +181 -0
- body/self_healing/header_service.py +308 -0
- body/self_healing/id_tagging_service.py +227 -0
- body/self_healing/knowledge_consolidation_service.py +80 -0
- body/self_healing/logging_service.py +292 -0
- body/self_healing/owner_tagging_service.py +5 -0
- body/self_healing/placeholder_fixer_service.py +27 -0
- body/self_healing/prune_private_capabilities.py +144 -0
- body/self_healing/purge_legacy_tags_service.py +146 -0
- body/self_healing/remediation_models.py +238 -0
- body/self_healing/test_context/__init__.py +10 -0
- body/self_healing/test_context/detectors.py +74 -0
- body/self_healing/test_context/examples.py +53 -0
- body/self_healing/test_context/formatter.py +65 -0
- body/self_healing/test_context/metrics.py +62 -0
- body/self_healing/test_context/models.py +83 -0
- body/self_healing/test_context/parsers.py +70 -0
- body/self_healing/test_context_analyzer.py +79 -0
- body/self_healing/test_failure_analyzer.py +259 -0
- body/self_healing/test_target_analyzer.py +118 -0
- body/self_healing/vulture_healer.py +161 -0
- body/services/__init__.py +4 -0
- body/services/artifact_service.py +155 -0
- body/services/blackboard_service/__init__.py +22 -0
- body/services/blackboard_service/blackboard_claim_service.py +216 -0
- body/services/blackboard_service/blackboard_proposal_service.py +305 -0
- body/services/blackboard_service/blackboard_query_service.py +333 -0
- body/services/blackboard_service/blackboard_service.py +602 -0
- body/services/capabilities.py +51 -0
- body/services/cim/__init__.py +22 -0
- body/services/cim/baselines.py +102 -0
- body/services/cim/census_service.py +190 -0
- body/services/cim/cim_constants.py +116 -0
- body/services/cim/cim_path_utils.py +123 -0
- body/services/cim/diff.py +152 -0
- body/services/cim/history.py +94 -0
- body/services/cim/models.py +319 -0
- body/services/cim/policy.py +149 -0
- body/services/cim/scanners.py +332 -0
- body/services/coherence_service.py +552 -0
- body/services/consequence_log_service.py +189 -0
- body/services/constitutional_rule_loader.py +141 -0
- body/services/constitutional_validator.py +366 -0
- body/services/crate_creation_service.py +217 -0
- body/services/crate_processing_service.py +279 -0
- body/services/crawl_service/__init__.py +10 -0
- body/services/crawl_service/main_module.py +531 -0
- body/services/crawl_service/orchestrator.py +452 -0
- body/services/crawl_service/symbol_processing.py +279 -0
- body/services/database_service.py +242 -0
- body/services/doc_service.py +57 -0
- body/services/file_service.py +260 -0
- body/services/governance_claims_service.py +240 -0
- body/services/governance_init.py +41 -0
- body/services/health_log_service.py +167 -0
- body/services/intent_schema_validator.py +211 -0
- body/services/llm_client.py +87 -0
- body/services/mind_state_service.py +129 -0
- body/services/policy_expression_evaluator.py +154 -0
- body/services/proposal_supervision_service.py +187 -0
- body/services/representation_coherence_service.py +209 -0
- body/services/service_registry.py +431 -0
- body/services/session_attached_service.py +51 -0
- body/services/symbol_query_service.py +218 -0
- body/services/symbol_service.py +94 -0
- body/services/validation/__init__.py +14 -0
- body/services/validation/python_validator.py +89 -0
- body/services/validation/validation_policies.py +121 -0
- body/services/worker_registry_service.py +184 -0
- body/validators/__init__.py +1 -0
- body/validators/logic_conservation_validator.py +201 -0
- body/workers/call_site_rewriter.py +527 -0
- body/workers/prompt_artifact_writer.py +323 -0
- cli/__init__.py +4 -0
- cli/admin_cli.py +98 -0
- cli/cli_user.py +121 -0
- cli/commands/__init__.py +4 -0
- cli/commands/audit_reporter.py +272 -0
- cli/commands/check/__init__.py +115 -0
- cli/commands/check/audit.py +130 -0
- cli/commands/check/converters.py +129 -0
- cli/commands/check/diagnostics_commands.py +70 -0
- cli/commands/check/formatters.py +310 -0
- cli/commands/check/imports.py +73 -0
- cli/commands/check/quality.py +71 -0
- cli/commands/check/quality_gates.py +107 -0
- cli/commands/check/rule.py +136 -0
- cli/commands/check/utils.py +28 -0
- cli/commands/check_atomic_actions.py +167 -0
- cli/commands/components.py +66 -0
- cli/commands/coverage/__init__.py +22 -0
- cli/commands/coverage/analysis_commands.py +87 -0
- cli/commands/coverage/check_commands.py +173 -0
- cli/commands/coverage/generation_commands.py +155 -0
- cli/commands/coverage/services/__init__.py +15 -0
- cli/commands/coverage/services/coverage_checker.py +50 -0
- cli/commands/coverage/services/coverage_reporter.py +81 -0
- cli/commands/coverage/services/gaps_analyzer.py +114 -0
- cli/commands/daemon.py +908 -0
- cli/commands/develop.py +127 -0
- cli/commands/diagnostics.py +32 -0
- cli/commands/fix/__init__.py +84 -0
- cli/commands/fix/all_commands.py +94 -0
- cli/commands/fix/atomic_actions.py +48 -0
- cli/commands/fix/audit.py +146 -0
- cli/commands/fix/body_ui.py +93 -0
- cli/commands/fix/code_style.py +52 -0
- cli/commands/fix/db_tools.py +90 -0
- cli/commands/fix/fix_ir.py +79 -0
- cli/commands/fix/handler_discovery.py +54 -0
- cli/commands/fix/imports.py +55 -0
- cli/commands/fix/list_commands.py +54 -0
- cli/commands/fix/metadata.py +160 -0
- cli/commands/fix/modularity.py +88 -0
- cli/commands/fix/settings_access.py +65 -0
- cli/commands/fix_governed.py +103 -0
- cli/commands/fix_logging.py +24 -0
- cli/commands/governance.py +294 -0
- cli/commands/guard.py +125 -0
- cli/commands/inspect/__init__.py +79 -0
- cli/commands/inspect/_helpers.py +226 -0
- cli/commands/inspect/analysis.py +158 -0
- cli/commands/inspect/decisions.py +140 -0
- cli/commands/inspect/diagnostics.py +126 -0
- cli/commands/inspect/drift.py +100 -0
- cli/commands/inspect/patterns.py +102 -0
- cli/commands/inspect/refusals.py +211 -0
- cli/commands/inspect/repo_census.py +175 -0
- cli/commands/inspect/status.py +48 -0
- cli/commands/interactive_test.py +85 -0
- cli/commands/mind.py +49 -0
- cli/commands/refactor.py +234 -0
- cli/commands/refactor_support/__init__.py +7 -0
- cli/commands/refactor_support/analyzer.py +107 -0
- cli/commands/refactor_support/config.py +67 -0
- cli/commands/refactor_support/display.py +170 -0
- cli/commands/refactor_support/recommendations.py +55 -0
- cli/commands/run.py +60 -0
- cli/commands/search.py +103 -0
- cli/commands/status.py +86 -0
- cli/commands/submit.py +23 -0
- cli/interactive.py +177 -0
- cli/logic/__init__.py +7 -0
- cli/logic/agent.py +110 -0
- cli/logic/audit_capability_domains.py +106 -0
- cli/logic/audit_renderer.py +150 -0
- cli/logic/autonomy/actions.py +36 -0
- cli/logic/autonomy/views.py +116 -0
- cli/logic/body_contracts_checker.py +23 -0
- cli/logic/build.py +27 -0
- cli/logic/byor.py +148 -0
- cli/logic/capability.py +27 -0
- cli/logic/cli_utils.py +210 -0
- cli/logic/context.py +7 -0
- cli/logic/db.py +154 -0
- cli/logic/db_manage.py +22 -0
- cli/logic/decision_inspect_logic.py +62 -0
- cli/logic/diagnostics.py +148 -0
- cli/logic/diagnostics_policy.py +100 -0
- cli/logic/diagnostics_registry.py +165 -0
- cli/logic/duplicates.py +19 -0
- cli/logic/embeddings_cli.py +43 -0
- cli/logic/governance/__init__.py +16 -0
- cli/logic/governance/engine.py +216 -0
- cli/logic/governance/forensics_service.py +67 -0
- cli/logic/governance/limb_status_service.py +83 -0
- cli/logic/governance/renderer.py +115 -0
- cli/logic/governance/traceability_service.py +78 -0
- cli/logic/governance_logic.py +33 -0
- cli/logic/hub/__init__.py +14 -0
- cli/logic/hub/app.py +107 -0
- cli/logic/hub/formatter.py +29 -0
- cli/logic/hub/introspection.py +20 -0
- cli/logic/hub/repository.py +17 -0
- cli/logic/init.py +22 -0
- cli/logic/interactive_test/__init__.py +21 -0
- cli/logic/interactive_test/session.py +102 -0
- cli/logic/interactive_test/steps/__init__.py +23 -0
- cli/logic/interactive_test/steps/execution.py +53 -0
- cli/logic/interactive_test/steps/generation.py +88 -0
- cli/logic/interactive_test/steps/healing.py +79 -0
- cli/logic/interactive_test/steps/utils.py +31 -0
- cli/logic/interactive_test/steps/verification.py +71 -0
- cli/logic/interactive_test/ui.py +203 -0
- cli/logic/interactive_test/workflow.py +168 -0
- cli/logic/interactive_test_logic.py +41 -0
- cli/logic/knowledge.py +47 -0
- cli/logic/legacy_scan_logic.py +306 -0
- cli/logic/list_audits.py +49 -0
- cli/logic/log_audit.py +53 -0
- cli/logic/new.py +8 -0
- cli/logic/project_docs.py +28 -0
- cli/logic/reconcile.py +127 -0
- cli/logic/refusal_inspect_logic.py +208 -0
- cli/logic/report.py +47 -0
- cli/logic/status.py +47 -0
- cli/logic/symbol_drift.py +77 -0
- cli/logic/sync.py +48 -0
- cli/logic/sync_domains.py +72 -0
- cli/logic/sync_manifest.py +31 -0
- cli/logic/tools.py +133 -0
- cli/logic/utils_migration.py +43 -0
- cli/logic/validate.py +107 -0
- cli/logic/vector_drift.py +109 -0
- cli/logic/yaml_processor.py +5 -0
- cli/renderers/audit_detail.py +37 -0
- cli/renderers/audit_overview.py +39 -0
- cli/resources/__init__.py +27 -0
- cli/resources/admin/__init__.py +23 -0
- cli/resources/admin/coverage.py +63 -0
- cli/resources/admin/debt_scan.py +195 -0
- cli/resources/admin/forensics.py +97 -0
- cli/resources/admin/health.py +66 -0
- cli/resources/admin/hub.py +9 -0
- cli/resources/admin/meta.py +47 -0
- cli/resources/admin/patterns.py +28 -0
- cli/resources/admin/refusals.py +31 -0
- cli/resources/admin/self_check.py +175 -0
- cli/resources/admin/status.py +66 -0
- cli/resources/admin/summary.py +67 -0
- cli/resources/admin/traces.py +46 -0
- cli/resources/admin/validate_env.py +56 -0
- cli/resources/code/__init__.py +31 -0
- cli/resources/code/actions.py +48 -0
- cli/resources/code/audit.py +416 -0
- cli/resources/code/audit_duplicates.py +22 -0
- cli/resources/code/check_imports.py +75 -0
- cli/resources/code/check_ui.py +62 -0
- cli/resources/code/clarity.py +51 -0
- cli/resources/code/complexity.py +55 -0
- cli/resources/code/docstrings.py +51 -0
- cli/resources/code/fix_atomic.py +51 -0
- cli/resources/code/format.py +62 -0
- cli/resources/code/hub.py +10 -0
- cli/resources/code/integrity.py +75 -0
- cli/resources/code/lint.py +37 -0
- cli/resources/code/logging.py +49 -0
- cli/resources/code/refactor.py +32 -0
- cli/resources/code/test.py +30 -0
- cli/resources/coherence/__init__.py +13 -0
- cli/resources/coherence/check.py +111 -0
- cli/resources/coherence/hub.py +13 -0
- cli/resources/coherence/repair_counts.py +73 -0
- cli/resources/coherence/report.py +144 -0
- cli/resources/coherence/seed.py +297 -0
- cli/resources/coherence/supersede.py +124 -0
- cli/resources/coherence/triage.py +81 -0
- cli/resources/constitution/__init__.py +19 -0
- cli/resources/constitution/audit.py +49 -0
- cli/resources/constitution/query.py +45 -0
- cli/resources/constitution/status.py +34 -0
- cli/resources/constitution/validate.py +36 -0
- cli/resources/context/__init__.py +21 -0
- cli/resources/context/build.py +374 -0
- cli/resources/context/cache.py +156 -0
- cli/resources/context/explain.py +140 -0
- cli/resources/context/hub.py +11 -0
- cli/resources/context/search.py +114 -0
- cli/resources/database/__init__.py +13 -0
- cli/resources/database/cleanup.py +107 -0
- cli/resources/database/export.py +61 -0
- cli/resources/database/hub.py +11 -0
- cli/resources/database/migrate.py +57 -0
- cli/resources/database/status.py +101 -0
- cli/resources/database/sync.py +84 -0
- cli/resources/database/sync_env.py +76 -0
- cli/resources/database/sync_registry.py +48 -0
- cli/resources/dev/__init__.py +14 -0
- cli/resources/dev/hub.py +11 -0
- cli/resources/dev/refactor.py +92 -0
- cli/resources/dev/stability.py +60 -0
- cli/resources/dev/strategic_audit.py +113 -0
- cli/resources/dev/sync.py +51 -0
- cli/resources/dev/test.py +34 -0
- cli/resources/intent/__init__.py +11 -0
- cli/resources/intent/hub.py +9 -0
- cli/resources/intent/sync_vocabulary.py +396 -0
- cli/resources/project/__init__.py +19 -0
- cli/resources/project/docs.py +41 -0
- cli/resources/project/new.py +40 -0
- cli/resources/project/onboard.py +31 -0
- cli/resources/proposals/__init__.py +28 -0
- cli/resources/proposals/create.py +82 -0
- cli/resources/proposals/integrate.py +49 -0
- cli/resources/proposals/list.py +76 -0
- cli/resources/proposals/manage.py +117 -0
- cli/resources/runtime/__init__.py +9 -0
- cli/resources/runtime/health.py +925 -0
- cli/resources/secrets/__init__.py +10 -0
- cli/resources/secrets/hub.py +9 -0
- cli/resources/secrets/manage.py +309 -0
- cli/resources/symbols/__init__.py +13 -0
- cli/resources/symbols/audit.py +46 -0
- cli/resources/symbols/fix_ids.py +43 -0
- cli/resources/symbols/hub.py +11 -0
- cli/resources/symbols/resolve_duplicates.py +30 -0
- cli/resources/symbols/sync.py +39 -0
- cli/resources/symbols/tag.py +47 -0
- cli/resources/vectors/__init__.py +11 -0
- cli/resources/vectors/cleanup.py +95 -0
- cli/resources/vectors/hub.py +9 -0
- cli/resources/vectors/query.py +105 -0
- cli/resources/vectors/status.py +30 -0
- cli/resources/vectors/sync.py +57 -0
- cli/resources/vectors/sync_code.py +67 -0
- cli/resources/workers/__init__.py +13 -0
- cli/resources/workers/blackboard.py +253 -0
- cli/resources/workers/remediate.py +337 -0
- cli/resources/workers/run.py +140 -0
- cli/utils/__init__.py +32 -0
- cli/utils/annotation_formatter.py +154 -0
- cli/utils/decorators.py +184 -0
- cli/utils/display.py +79 -0
- cli/utils/exit_codes.py +59 -0
- cli/utils/helpers.py +34 -0
- cli/utils/prompts.py +21 -0
- core_runtime-0.1.1.dist-info/LICENSE +21 -0
- core_runtime-0.1.1.dist-info/METADATA +346 -0
- core_runtime-0.1.1.dist-info/RECORD +962 -0
- core_runtime-0.1.1.dist-info/WHEEL +4 -0
- core_runtime-0.1.1.dist-info/entry_points.txt +4 -0
- mind/__init__.py +4 -0
- mind/coherence/__init__.py +1 -0
- mind/coherence/checker.py +314 -0
- mind/coherence/checks/__init__.py +4 -0
- mind/coherence/checks/base.py +34 -0
- mind/coherence/checks/r1_scoped.py +228 -0
- mind/coherence/checks/row2_grounding.py +69 -0
- mind/coherence/checks/row3_citation.py +119 -0
- mind/coherence/checks/row4_naming.py +126 -0
- mind/coherence/checks/sameconcern.py +136 -0
- mind/coherence/checks/specgap.py +233 -0
- mind/coherence/checks/vocabulary.py +112 -0
- mind/coherence/llm_judge.py +207 -0
- mind/enforcement/audit.py +78 -0
- mind/governance/__init__.py +22 -0
- mind/governance/assumption_extractor.py +326 -0
- mind/governance/audit_context.py +520 -0
- mind/governance/audit_postprocessor.py +36 -0
- mind/governance/audit_report_writer.py +46 -0
- mind/governance/audit_types.py +98 -0
- mind/governance/auditor.py +179 -0
- mind/governance/authority_package_builder.py +502 -0
- mind/governance/constitutional_auditor_dynamic.py +362 -0
- mind/governance/enforcement/__init__.py +24 -0
- mind/governance/enforcement_loader.py +245 -0
- mind/governance/enforcement_methods.py +28 -0
- mind/governance/entry_point_policy.py +62 -0
- mind/governance/executable_rule.py +127 -0
- mind/governance/filtered_audit.py +272 -0
- mind/governance/finding_processor.py +76 -0
- mind/governance/meta_validator.py +259 -0
- mind/governance/micro_proposal_validator.py +169 -0
- mind/governance/policy_coverage_service.py +183 -0
- mind/governance/policy_rule.py +94 -0
- mind/governance/rule_conflict_detector.py +127 -0
- mind/governance/rule_executor.py +306 -0
- mind/governance/rule_extractor.py +376 -0
- mind/governance/stateless_audit.py +206 -0
- mind/governance/violation_report.py +136 -0
- mind/logic/engines/_knowledge_gate_duplication.py +117 -0
- mind/logic/engines/action_gate.py +76 -0
- mind/logic/engines/artifact_gate.py +1168 -0
- mind/logic/engines/ast_gate/__init__.py +9 -0
- mind/logic/engines/ast_gate/base.py +263 -0
- mind/logic/engines/ast_gate/checks/__init__.py +35 -0
- mind/logic/engines/ast_gate/checks/async_checks.py +276 -0
- mind/logic/engines/ast_gate/checks/capability_checks.py +197 -0
- mind/logic/engines/ast_gate/checks/conservation_checks.py +126 -0
- mind/logic/engines/ast_gate/checks/generic_checks.py +244 -0
- mind/logic/engines/ast_gate/checks/import_checks.py +164 -0
- mind/logic/engines/ast_gate/checks/knowledge_source_check.py +88 -0
- mind/logic/engines/ast_gate/checks/logging_checks.py +133 -0
- mind/logic/engines/ast_gate/checks/metadata_checks.py +242 -0
- mind/logic/engines/ast_gate/checks/modularity_checks.py +436 -0
- mind/logic/engines/ast_gate/checks/naming_checks.py +120 -0
- mind/logic/engines/ast_gate/checks/prompt_model_checks.py +83 -0
- mind/logic/engines/ast_gate/checks/protected_namespace_access_check.py +298 -0
- mind/logic/engines/ast_gate/checks/purity_checks.py +421 -0
- mind/logic/engines/ast_gate/checks/purity_enforcement_check.py +56 -0
- mind/logic/engines/ast_gate/checks/runtime_import_boundary.py +198 -0
- mind/logic/engines/ast_gate/checks/schema_conformance_checks.py +130 -0
- mind/logic/engines/ast_gate/engine.py +317 -0
- mind/logic/engines/ast_gate.py +19 -0
- mind/logic/engines/base.py +106 -0
- mind/logic/engines/cli_gate/__init__.py +25 -0
- mind/logic/engines/cli_gate/base_check.py +45 -0
- mind/logic/engines/cli_gate/checks/__init__.py +31 -0
- mind/logic/engines/cli_gate/checks/async_execution.py +65 -0
- mind/logic/engines/cli_gate/checks/dangerous_explicit.py +60 -0
- mind/logic/engines/cli_gate/checks/discovery_strict.py +167 -0
- mind/logic/engines/cli_gate/checks/help_required.py +43 -0
- mind/logic/engines/cli_gate/checks/no_duplicates.py +57 -0
- mind/logic/engines/cli_gate/checks/no_layer_exposure.py +48 -0
- mind/logic/engines/cli_gate/checks/resource_first.py +58 -0
- mind/logic/engines/cli_gate/checks/standard_verbs.py +50 -0
- mind/logic/engines/cli_gate/engine.py +211 -0
- mind/logic/engines/glob_gate.py +214 -0
- mind/logic/engines/knowledge_gate.py +410 -0
- mind/logic/engines/llm_gate.py +407 -0
- mind/logic/engines/llm_gate_stub.py +67 -0
- mind/logic/engines/passive_gate.py +45 -0
- mind/logic/engines/regex_gate.py +97 -0
- mind/logic/engines/registry.py +134 -0
- mind/logic/engines/runtime_gate.py +265 -0
- mind/logic/engines/taxonomy_gate.py +229 -0
- mind/logic/engines/workflow_gate/__init__.py +22 -0
- mind/logic/engines/workflow_gate/base_check.py +42 -0
- mind/logic/engines/workflow_gate/checks/__init__.py +32 -0
- mind/logic/engines/workflow_gate/checks/alignment.py +93 -0
- mind/logic/engines/workflow_gate/checks/audit.py +80 -0
- mind/logic/engines/workflow_gate/checks/canary.py +47 -0
- mind/logic/engines/workflow_gate/checks/coverage.py +74 -0
- mind/logic/engines/workflow_gate/checks/dead_code.py +53 -0
- mind/logic/engines/workflow_gate/checks/import_resolution.py +97 -0
- mind/logic/engines/workflow_gate/checks/linter.py +104 -0
- mind/logic/engines/workflow_gate/checks/quality.py +49 -0
- mind/logic/engines/workflow_gate/checks/ruff_format.py +99 -0
- mind/logic/engines/workflow_gate/checks/tests.py +81 -0
- mind/logic/engines/workflow_gate/engine.py +207 -0
- shared/__init__.py +32 -0
- shared/action_logger.py +61 -0
- shared/action_types.py +199 -0
- shared/activity_logging.py +149 -0
- shared/ai/constitutional_envelope.py +341 -0
- shared/ai/prompt_model.py +585 -0
- shared/ai/response_parser.py +191 -0
- shared/ast_utility.py +279 -0
- shared/atomic_action.py +117 -0
- shared/cli/__init__.py +6 -0
- shared/cli/app_introspection.py +134 -0
- shared/cli/command_meta.py +224 -0
- shared/cli_types.py +92 -0
- shared/component_primitive.py +216 -0
- shared/config.py +216 -0
- shared/config_loader.py +54 -0
- shared/context.py +113 -0
- shared/exceptions.py +100 -0
- shared/governance/__init__.py +9 -0
- shared/governance/coherence_harvester.py +258 -0
- shared/governance_token.py +77 -0
- shared/infrastructure/__init__.py +5 -0
- shared/infrastructure/adapters/__init__.py +5 -0
- shared/infrastructure/adapters/embedding_provider.py +66 -0
- shared/infrastructure/bootstrap_registry.py +57 -0
- shared/infrastructure/clients/__init__.py +5 -0
- shared/infrastructure/clients/qdrant_client.py +455 -0
- shared/infrastructure/config_service.py +382 -0
- shared/infrastructure/config_validator.py +70 -0
- shared/infrastructure/context/__init__.py +45 -0
- shared/infrastructure/context/builder.py +804 -0
- shared/infrastructure/context/cache.py +101 -0
- shared/infrastructure/context/database.py +205 -0
- shared/infrastructure/context/limb_workspace.py +134 -0
- shared/infrastructure/context/models.py +65 -0
- shared/infrastructure/context/providers/__init__.py +18 -0
- shared/infrastructure/context/providers/ast.py +211 -0
- shared/infrastructure/context/providers/db.py +165 -0
- shared/infrastructure/context/providers/vectors.py +195 -0
- shared/infrastructure/context/redactor.py +109 -0
- shared/infrastructure/context/serializers.py +144 -0
- shared/infrastructure/context/service.py +271 -0
- shared/infrastructure/context/validator.py +247 -0
- shared/infrastructure/database/models/__init__.py +80 -0
- shared/infrastructure/database/models/autonomous_proposals.py +83 -0
- shared/infrastructure/database/models/decision_traces.py +129 -0
- shared/infrastructure/database/models/governance.py +183 -0
- shared/infrastructure/database/models/knowledge.py +205 -0
- shared/infrastructure/database/models/learning.py +99 -0
- shared/infrastructure/database/models/llm_config.py +274 -0
- shared/infrastructure/database/models/operations.py +152 -0
- shared/infrastructure/database/models/refusals.py +147 -0
- shared/infrastructure/database/models/system.py +124 -0
- shared/infrastructure/database/models/vectors.py +113 -0
- shared/infrastructure/database/models/workers.py +96 -0
- shared/infrastructure/database/session_manager.py +166 -0
- shared/infrastructure/diagnostic_service.py +103 -0
- shared/infrastructure/events/__init__.py +5 -0
- shared/infrastructure/events/base.py +44 -0
- shared/infrastructure/events/bus.py +76 -0
- shared/infrastructure/git_service.py +385 -0
- shared/infrastructure/intent/action_risk.py +135 -0
- shared/infrastructure/intent/audit_verdict.py +131 -0
- shared/infrastructure/intent/autonomy_dirty_tree.py +104 -0
- shared/infrastructure/intent/canonical_enums.py +116 -0
- shared/infrastructure/intent/cognitive_roles.py +96 -0
- shared/infrastructure/intent/errors.py +13 -0
- shared/infrastructure/intent/filesystem_operations.py +359 -0
- shared/infrastructure/intent/intent_connector.py +157 -0
- shared/infrastructure/intent/intent_repository.py +707 -0
- shared/infrastructure/intent/intent_validator.py +248 -0
- shared/infrastructure/intent/operational_capabilities.py +470 -0
- shared/infrastructure/intent/operational_config.py +1343 -0
- shared/infrastructure/intent/operational_mode.py +66 -0
- shared/infrastructure/intent/task_type_phases.py +180 -0
- shared/infrastructure/intent/test_coverage_paths.py +181 -0
- shared/infrastructure/intent/vocabulary_projection.py +241 -0
- shared/infrastructure/knowledge/knowledge_service.py +155 -0
- shared/infrastructure/knowledge_graph_service.py +214 -0
- shared/infrastructure/llm/client.py +448 -0
- shared/infrastructure/llm/client_registry.py +153 -0
- shared/infrastructure/llm/fallback_client.py +144 -0
- shared/infrastructure/llm/providers/anthropic.py +113 -0
- shared/infrastructure/llm/providers/base.py +130 -0
- shared/infrastructure/llm/providers/ollama.py +265 -0
- shared/infrastructure/llm/providers/openai.py +168 -0
- shared/infrastructure/repositories/__init__.py +5 -0
- shared/infrastructure/repositories/db/__init__.py +5 -0
- shared/infrastructure/repositories/db/common.py +138 -0
- shared/infrastructure/repositories/db/engine.py +26 -0
- shared/infrastructure/repositories/db/migration_service.py +75 -0
- shared/infrastructure/repositories/db/status_service.py +61 -0
- shared/infrastructure/repositories/memory_repository.py +134 -0
- shared/infrastructure/repositories/symbol_definition_repository.py +170 -0
- shared/infrastructure/repositories/task_repository.py +59 -0
- shared/infrastructure/repositories/vector_link_repository.py +83 -0
- shared/infrastructure/rooted_repository.py +50 -0
- shared/infrastructure/secrets_service.py +317 -0
- shared/infrastructure/specs/__init__.py +9 -0
- shared/infrastructure/specs/specs_repository.py +100 -0
- shared/infrastructure/storage/file_classifier.py +32 -0
- shared/infrastructure/storage/file_handler.py +330 -0
- shared/infrastructure/storage/file_provider.py +233 -0
- shared/infrastructure/storage/integrity_service.py +108 -0
- shared/infrastructure/validation/black_formatter.py +41 -0
- shared/infrastructure/validation/quality.py +46 -0
- shared/infrastructure/validation/ruff_linter.py +104 -0
- shared/infrastructure/validation/syntax_checker.py +45 -0
- shared/infrastructure/validation/test_runner.py +167 -0
- shared/infrastructure/validation/yaml_validator.py +45 -0
- shared/infrastructure/vector/__init__.py +15 -0
- shared/infrastructure/vector/adapters/__init__.py +20 -0
- shared/infrastructure/vector/adapters/constitutional/__init__.py +26 -0
- shared/infrastructure/vector/adapters/constitutional/chunker.py +246 -0
- shared/infrastructure/vector/adapters/constitutional/doc_key_resolver.py +109 -0
- shared/infrastructure/vector/adapters/constitutional/item_builder.py +158 -0
- shared/infrastructure/vector/adapters/constitutional/utils.py +39 -0
- shared/infrastructure/vector/adapters/constitutional_adapter.py +309 -0
- shared/infrastructure/vector/adapters/specs_adapter.py +171 -0
- shared/infrastructure/vector/cognitive_adapter.py +119 -0
- shared/infrastructure/vector/vector_index_service.py +230 -0
- shared/logger.py +195 -0
- shared/models/__init__.py +47 -0
- shared/models/action_result.py +77 -0
- shared/models/audit_models.py +106 -0
- shared/models/audit_rendering.py +30 -0
- shared/models/capability_models.py +22 -0
- shared/models/constitutional_validation.py +185 -0
- shared/models/drift_models.py +24 -0
- shared/models/embedding_payload.py +45 -0
- shared/models/execution_models.py +71 -0
- shared/models/pattern_graph.py +62 -0
- shared/models/prompt_model.py +244 -0
- shared/models/refusal_result.py +294 -0
- shared/models/remediation.py +35 -0
- shared/models/validation_result.py +34 -0
- shared/models/vector_models.py +62 -0
- shared/models/workflow_models.py +242 -0
- shared/path_resolver.py +408 -0
- shared/path_utils.py +60 -0
- shared/processors/base_processor.py +171 -0
- shared/processors/yaml_processor.py +72 -0
- shared/protocols/__init__.py +21 -0
- shared/protocols/brain_services.py +35 -0
- shared/protocols/cognitive.py +35 -0
- shared/protocols/executor.py +47 -0
- shared/protocols/interpreter.py +24 -0
- shared/protocols/knowledge.py +21 -0
- shared/protocols/llm.py +25 -0
- shared/protocols/typer_protocols.py +37 -0
- shared/time.py +18 -0
- shared/universal.py +43 -0
- shared/utils/__init__.py +81 -0
- shared/utils/audit_grouping.py +50 -0
- shared/utils/common_knowledge.py +82 -0
- shared/utils/domain_mapper.py +61 -0
- shared/utils/embedding_utils.py +182 -0
- shared/utils/glob_match.py +93 -0
- shared/utils/header_tools.py +177 -0
- shared/utils/parallel_processor.py +60 -0
- shared/utils/parsing.py +189 -0
- shared/utils/path_utils.py +268 -0
- shared/utils/subprocess_utils.py +87 -0
- shared/workers/__init__.py +19 -0
- shared/workers/base.py +533 -0
- shared/workers/declaration_validator.py +124 -0
- shared/workers/loop_hold_telemetry.py +210 -0
- shared/workers/schedule.py +123 -0
- will/__init__.py +4 -0
- will/agents/__init__.py +4 -0
- will/agents/base_planner.py +133 -0
- will/agents/code_generation/__init__.py +15 -0
- will/agents/code_generation/code_generator.py +247 -0
- will/agents/code_generation/context_formatters.py +99 -0
- will/agents/code_generation/correction_engine.py +157 -0
- will/agents/code_generation/extraction.py +165 -0
- will/agents/code_generation/pattern_validator.py +264 -0
- will/agents/code_generation/prompt_builders.py +209 -0
- will/agents/coder_agent.py +229 -0
- will/agents/coder_agent_refusal_handler.py +168 -0
- will/agents/cognitive_orchestrator.py +152 -0
- will/agents/conversational/__init__.py +13 -0
- will/agents/conversational/agent.py +271 -0
- will/agents/conversational/factory.py +72 -0
- will/agents/conversational/helpers.py +116 -0
- will/agents/execution_agent.py +294 -0
- will/agents/governance_mixin.py +132 -0
- will/agents/planner_agent.py +261 -0
- will/agents/resource_selector.py +228 -0
- will/agents/strategic_auditor/__init__.py +15 -0
- will/agents/strategic_auditor/agent.py +199 -0
- will/agents/strategic_auditor/context_gatherer.py +586 -0
- will/agents/strategic_auditor/effects.py +136 -0
- will/agents/strategic_auditor/models.py +61 -0
- will/agents/strategic_auditor/reasoning.py +169 -0
- will/agents/tagger_agent.py +294 -0
- will/agents/traced_agent_mixin.py +33 -0
- will/autonomy/__init__.py +20 -0
- will/autonomy/autonomous_developer.py +127 -0
- will/autonomy/proposal.py +585 -0
- will/autonomy/proposal_execution_pipeline.py +264 -0
- will/autonomy/proposal_executor.py +410 -0
- will/autonomy/proposal_mapper.py +187 -0
- will/autonomy/proposal_repository.py +118 -0
- will/autonomy/proposal_service.py +251 -0
- will/autonomy/proposal_state_manager.py +239 -0
- will/deciders/__init__.py +9 -0
- will/deciders/governance_decider.py +207 -0
- will/governance/__init__.py +13 -0
- will/governance/audit_remediation_runner.py +197 -0
- will/governance/audit_runner.py +368 -0
- will/governance/census_runner.py +278 -0
- will/governance/coverage_runner.py +489 -0
- will/governance/daemon_runner.py +164 -0
- will/governance/fix_runner.py +693 -0
- will/governance/inspect_runner.py +521 -0
- will/governance/integrity_runner.py +66 -0
- will/governance/lint_runner.py +93 -0
- will/governance/refactor_runner.py +372 -0
- will/governance/sync_runner.py +186 -0
- will/interpreters/__init__.py +44 -0
- will/interpreters/cli_args_interpreter.py +106 -0
- will/interpreters/natural_language_interpreter.py +276 -0
- will/interpreters/request_interpreter.py +126 -0
- will/lifecycle/__init__.py +12 -0
- will/lifecycle/integration_runner.py +54 -0
- will/maintenance/metadata_scribe_service.py +64 -0
- will/orchestration/__init__.py +4 -0
- will/orchestration/cognitive_service.py +185 -0
- will/orchestration/decision_tracer.py +257 -0
- will/orchestration/intent_alignment.py +59 -0
- will/orchestration/intent_guard.py +20 -0
- will/orchestration/phase_registry.py +92 -0
- will/orchestration/process_orchestrator.py +270 -0
- will/orchestration/prompt_pipeline.py +174 -0
- will/orchestration/remediation_orchestrator.py +292 -0
- will/orchestration/self_correction_engine.py +154 -0
- will/orchestration/validation_pipeline.py +45 -0
- will/orchestration/workflow_orchestrator.py +277 -0
- will/phases/__init__.py +24 -0
- will/phases/audit_phase.py +152 -0
- will/phases/canary/pytest_runner.py +156 -0
- will/phases/canary/result_builder.py +130 -0
- will/phases/canary/test_discovery.py +104 -0
- will/phases/canary_validation_phase.py +188 -0
- will/phases/code_generation/artifact_saver.py +175 -0
- will/phases/code_generation/code_sensor.py +117 -0
- will/phases/code_generation/file_path_extractor.py +53 -0
- will/phases/code_generation/work_directory_manager.py +83 -0
- will/phases/code_generation_phase.py +480 -0
- will/phases/execution_phase.py +179 -0
- will/phases/interpret_phase.py +266 -0
- will/phases/load_phase.py +102 -0
- will/phases/parse_phase.py +117 -0
- will/phases/planning_phase.py +182 -0
- will/phases/runtime_phase.py +112 -0
- will/phases/sandbox_validation_phase.py +130 -0
- will/phases/style_check_phase.py +209 -0
- will/phases/test_generation_phase.py +159 -0
- will/self_healing/alignment/persistence.py +53 -0
- will/self_healing/alignment/sandbox.py +51 -0
- will/self_healing/alignment/specialists.py +165 -0
- will/self_healing/alignment_orchestrator.py +140 -0
- will/self_healing/audit_remediation_service.py +199 -0
- will/self_healing/batch_remediation_service.py +280 -0
- will/self_healing/capability_reconciliation_service.py +96 -0
- will/self_healing/capability_tagging_service.py +224 -0
- will/self_healing/clarity_service.py +204 -0
- will/self_healing/complexity_service.py +260 -0
- will/self_healing/context_aware_test_generator.py +298 -0
- will/self_healing/coverage_remediation_service.py +97 -0
- will/self_healing/coverage_watcher.py +192 -0
- will/self_healing/enrichment_service.py +211 -0
- will/self_healing/linelength_service.py +168 -0
- will/self_healing/modularity_remediation_service.py +203 -0
- will/self_healing/remediation_evidence_writer.py +144 -0
- will/self_healing/remediation_executor.py +113 -0
- will/self_healing/remediation_interpretation/__init__.py +1 -0
- will/self_healing/remediation_interpretation/file_context_assembler.py +383 -0
- will/self_healing/remediation_interpretation/file_role_detector.py +599 -0
- will/self_healing/remediation_interpretation/finding_normalizer.py +221 -0
- will/self_healing/remediation_interpretation/models.py +198 -0
- will/self_healing/remediation_interpretation/reasoning_brief_builder.py +235 -0
- will/self_healing/remediation_interpretation/responsibility_extractor.py +421 -0
- will/self_healing/remediation_interpretation/service.py +202 -0
- will/self_healing/remediation_interpretation/strategy_catalog.py +252 -0
- will/self_healing/remediation_interpretation/strategy_selector.py +395 -0
- will/self_healing/remediation_pattern_matcher.py +126 -0
- will/self_healing/simple_test_generator.py +252 -0
- will/self_healing/single_file_remediation.py +137 -0
- will/self_healing/test_generation/automatic_repair.py +377 -0
- will/self_healing/test_generation/code_extractor.py +238 -0
- will/self_healing/test_generation/context_builder.py +140 -0
- will/self_healing/test_generation/executor.py +64 -0
- will/self_healing/test_generation/failure_parser.py +37 -0
- will/self_healing/test_generation/generation_workflow.py +140 -0
- will/self_healing/test_generation/generator.py +283 -0
- will/self_healing/test_generation/llm_correction.py +160 -0
- will/self_healing/test_generation/repair_workflow.py +128 -0
- will/self_healing/test_generation/single_test_fixer.py +77 -0
- will/self_healing/test_generation/test_extractor.py +50 -0
- will/self_healing/test_generation/test_scorer.py +53 -0
- will/self_healing/test_generation/test_validator.py +64 -0
- will/self_healing/test_generator.py +12 -0
- will/strategists/__init__.py +46 -0
- will/strategists/base_strategist.py +29 -0
- will/strategists/clarity_strategist.py +55 -0
- will/strategists/complexity_strategist.py +55 -0
- will/strategists/fix_strategist.py +467 -0
- will/strategists/sync_strategist.py +369 -0
- will/strategists/test_strategist.py +140 -0
- will/strategists/validation_strategist.py +315 -0
- will/test_generation/__init__.py +46 -0
- will/test_generation/adaptive_test_generator.py +123 -0
- will/test_generation/artifacts.py +102 -0
- will/test_generation/harness_detection.py +103 -0
- will/test_generation/helpers/__init__.py +14 -0
- will/test_generation/helpers/context_extractor.py +100 -0
- will/test_generation/helpers/test_executor.py +230 -0
- will/test_generation/llm_output.py +62 -0
- will/test_generation/models.py +25 -0
- will/test_generation/persistence.py +248 -0
- will/test_generation/phases/__init__.py +16 -0
- will/test_generation/phases/generation_phase.py +150 -0
- will/test_generation/phases/load_phase.py +53 -0
- will/test_generation/phases/parse_phase.py +50 -0
- will/test_generation/prompt_engine.py +123 -0
- will/test_generation/result_aggregator.py +73 -0
- will/test_generation/sandbox.py +203 -0
- will/test_generation/strategy_link.py +56 -0
- will/test_generation/test_extractor.py +140 -0
- will/test_generation/validation.py +89 -0
- will/tools/__init__.py +13 -0
- will/tools/anchor_builder.py +111 -0
- will/tools/anchor_search.py +73 -0
- will/tools/anchors/discovery.py +55 -0
- will/tools/anchors/storage.py +32 -0
- will/tools/architectural_context_builder.py +94 -0
- will/tools/cognitive_toolbox.py +47 -0
- will/tools/context/code_snippet_extractor.py +83 -0
- will/tools/context/embedding_search.py +76 -0
- will/tools/context/formatter.py +87 -0
- will/tools/context/models.py +38 -0
- will/tools/context/query_builder.py +42 -0
- will/tools/context/retriever.py +182 -0
- will/tools/context/standards.py +53 -0
- will/tools/file_navigator.py +180 -0
- will/tools/layers.py +31 -0
- will/tools/module_anchor_generator.py +137 -0
- will/tools/module_descriptor.py +154 -0
- will/tools/policy_vectorizer.py +184 -0
- will/tools/symbol_finder.py +188 -0
- will/tools/tool_generator.py +103 -0
- will/workers/audit_ingest_worker.py +207 -0
- will/workers/audit_violation_filter.py +108 -0
- will/workers/audit_violation_normalizer.py +108 -0
- will/workers/audit_violation_sensor.py +370 -0
- will/workers/blackboard_shop_manager.py +280 -0
- will/workers/capability_tagger.py +153 -0
- will/workers/circuit_breaker.py +335 -0
- will/workers/coherence_sensor.py +212 -0
- will/workers/commit_reachability_auditor.py +135 -0
- will/workers/db_sync_worker.py +96 -0
- will/workers/governance_embedding/__init__.py +9 -0
- will/workers/governance_embedding/governance_embedder_worker.py +189 -0
- will/workers/intent_inspector.py +599 -0
- will/workers/observer_worker.py +158 -0
- will/workers/prompt_extractor_worker.py +241 -0
- will/workers/proposal_consumer_effects.py +216 -0
- will/workers/proposal_consumer_revival.py +131 -0
- will/workers/proposal_consumer_worker.py +271 -0
- will/workers/proposal_pipeline_shop_manager.py +273 -0
- will/workers/repo_crawler.py +171 -0
- will/workers/repo_embedding/__init__.py +18 -0
- will/workers/repo_embedding/helpers.py +245 -0
- will/workers/repo_embedding/repo_embedder_workers.py +163 -0
- will/workers/test_coverage_sensor.py +208 -0
- will/workers/test_remediator/__init__.py +12 -0
- will/workers/test_remediator/_operations.py +295 -0
- will/workers/test_remediator/worker.py +187 -0
- will/workers/test_runner_sensor.py +430 -0
- will/workers/violation_executor.py +496 -0
- will/workers/violation_remediator.py +394 -0
- will/workers/violation_remediator_blackboard.py +225 -0
- will/workers/violation_remediator_body/__init__.py +12 -0
- will/workers/violation_remediator_body/blackboard.py +139 -0
- will/workers/violation_remediator_body/ceremony.py +154 -0
- will/workers/violation_remediator_body/context.py +70 -0
- will/workers/violation_remediator_body/llm.py +112 -0
- will/workers/violation_remediator_body/models.py +35 -0
- will/workers/violation_remediator_body/worker.py +518 -0
- will/workers/violation_remediator_proposal.py +328 -0
- will/workers/worker_shop_manager.py +244 -0
- will/workflows/__init__.py +23 -0
- will/workflows/dev_sync_reporter.py +274 -0
- will/workflows/dev_sync_workflow.py +118 -0
|
@@ -0,0 +1,1142 @@
|
|
|
1
|
+
# src/body/atomic/fix_actions.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Atomic Fix Actions - Code Remediation
|
|
5
|
+
|
|
6
|
+
Each action does ONE thing and returns ActionResult.
|
|
7
|
+
Actions are composable, auditable, and constitutionally governed.
|
|
8
|
+
|
|
9
|
+
Constitutional Alignment:
|
|
10
|
+
- Boundary: Uses CoreContext for repo_path (no direct settings access)
|
|
11
|
+
- Circularity Fix: Feature-level imports are performed inside functions.
|
|
12
|
+
- Remediation: Each action declares which audit check_ids it fixes via remediates=[].
|
|
13
|
+
ViolationRemediatorWorker uses this to close the autonomous audit loop.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
import time
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import TYPE_CHECKING, Any
|
|
22
|
+
|
|
23
|
+
from body.atomic.registry import ActionCategory, register_action
|
|
24
|
+
from mind.governance.violation_report import ConstitutionalViolationError
|
|
25
|
+
from shared.action_types import ActionImpact, ActionResult
|
|
26
|
+
from shared.atomic_action import atomic_action
|
|
27
|
+
from shared.logger import getLogger
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from shared.context import CoreContext
|
|
32
|
+
|
|
33
|
+
logger = getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
_RUNTIME_DIR_PATTERN = re.compile(
|
|
36
|
+
r"""["'](?:reports|logs)/|/\s*["'](?:reports|logs)["']"""
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _error_data(exc: Exception, **extra: Any) -> dict[str, Any]:
|
|
41
|
+
"""Build ActionResult.data for an exception, preserving structure when available.
|
|
42
|
+
|
|
43
|
+
ConstitutionalViolationError carries the full list[ViolationReport] that
|
|
44
|
+
IntentGuard produced; we serialize it via to_dict() so rule_name, path,
|
|
45
|
+
and source_policy survive the persistence hop into proposal.execution_results.
|
|
46
|
+
Any other exception type degrades cleanly to the legacy flat {"error": str(e)}
|
|
47
|
+
shape, so this helper is safe to adopt per-action without coordinated changes
|
|
48
|
+
elsewhere.
|
|
49
|
+
"""
|
|
50
|
+
if isinstance(exc, ConstitutionalViolationError):
|
|
51
|
+
return {**exc.to_dict(), **extra}
|
|
52
|
+
return {"error": str(exc), **extra}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@register_action(
|
|
56
|
+
action_id="fix.format",
|
|
57
|
+
description="Format code with ruff format and ruff check",
|
|
58
|
+
category=ActionCategory.FIX,
|
|
59
|
+
policies=["rules/code/purity"],
|
|
60
|
+
remediates=["style.formatter_required"],
|
|
61
|
+
)
|
|
62
|
+
@atomic_action(
|
|
63
|
+
action_id="fix.format",
|
|
64
|
+
intent="Atomic action for action_format_code",
|
|
65
|
+
impact=ActionImpact.WRITE_CODE,
|
|
66
|
+
policies=["atomic_actions"],
|
|
67
|
+
)
|
|
68
|
+
# ID: 5c3ede6c-23e1-4b92-8a00-7b2046eac121
|
|
69
|
+
async def action_format_code(
|
|
70
|
+
file_path: str | None = None, write: bool = False, **kwargs
|
|
71
|
+
) -> ActionResult:
|
|
72
|
+
"""Format code using ruff format and ruff check."""
|
|
73
|
+
start = time.time()
|
|
74
|
+
from body.self_healing.code_style_service import format_code
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
format_code(path=file_path, write=write)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
return ActionResult(
|
|
80
|
+
action_id="fix.format",
|
|
81
|
+
ok=False,
|
|
82
|
+
data={"error": str(e), "write": write},
|
|
83
|
+
duration_sec=time.time() - start,
|
|
84
|
+
)
|
|
85
|
+
return ActionResult(
|
|
86
|
+
action_id="fix.format",
|
|
87
|
+
ok=True,
|
|
88
|
+
data={"formatted": True, "write": write},
|
|
89
|
+
duration_sec=time.time() - start,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@register_action(
|
|
94
|
+
action_id="fix.imports",
|
|
95
|
+
description="Sort and group imports according to PEP 8 / Constitutional standards",
|
|
96
|
+
category=ActionCategory.FIX,
|
|
97
|
+
policies=["rules/code/purity"],
|
|
98
|
+
remediates=["style.import_order", "style.no_unused_imports"],
|
|
99
|
+
)
|
|
100
|
+
@atomic_action(
|
|
101
|
+
action_id="fix.imports",
|
|
102
|
+
intent="Standardize Python import blocks using Ruff",
|
|
103
|
+
impact=ActionImpact.WRITE_METADATA,
|
|
104
|
+
policies=["atomic_actions"],
|
|
105
|
+
)
|
|
106
|
+
# ID: d5879178-fdfe-4d8b-b6a6-f887f8e9500b
|
|
107
|
+
async def action_fix_imports(write: bool = False, **kwargs) -> ActionResult:
|
|
108
|
+
"""Sort and group Python imports according to constitutional style policy."""
|
|
109
|
+
start = time.time()
|
|
110
|
+
from shared.utils.subprocess_utils import run_poetry_command
|
|
111
|
+
|
|
112
|
+
target_path = "src/"
|
|
113
|
+
try:
|
|
114
|
+
cmd = ["ruff", "check", target_path, "--select", "I"]
|
|
115
|
+
if write:
|
|
116
|
+
cmd.append("--fix")
|
|
117
|
+
cmd.append("--exit-zero")
|
|
118
|
+
|
|
119
|
+
run_poetry_command(f"Sorting imports in {target_path}", cmd)
|
|
120
|
+
|
|
121
|
+
return ActionResult(
|
|
122
|
+
action_id="fix.imports",
|
|
123
|
+
ok=True,
|
|
124
|
+
data={"status": "completed", "target": target_path, "write": write},
|
|
125
|
+
duration_sec=time.time() - start,
|
|
126
|
+
)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
return ActionResult(
|
|
129
|
+
action_id="fix.imports",
|
|
130
|
+
ok=False,
|
|
131
|
+
data={"error": str(e)},
|
|
132
|
+
duration_sec=time.time() - start,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@register_action(
|
|
137
|
+
action_id="fix.headers",
|
|
138
|
+
description="Fix file headers to match constitutional standards",
|
|
139
|
+
category=ActionCategory.FIX,
|
|
140
|
+
policies=["rules/code/purity"],
|
|
141
|
+
remediates=["layout.src_module_header"],
|
|
142
|
+
)
|
|
143
|
+
@atomic_action(
|
|
144
|
+
action_id="fix.headers",
|
|
145
|
+
intent="Atomic action for action_fix_headers",
|
|
146
|
+
impact=ActionImpact.WRITE_CODE,
|
|
147
|
+
policies=["atomic_actions"],
|
|
148
|
+
)
|
|
149
|
+
# ID: 7d43d782-739a-47d3-ae3e-c36047ad9867
|
|
150
|
+
async def action_fix_headers(
|
|
151
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
152
|
+
) -> ActionResult:
|
|
153
|
+
"""Fix file headers."""
|
|
154
|
+
from body.self_healing.header_service import fix_headers_internal
|
|
155
|
+
|
|
156
|
+
return await fix_headers_internal(core_context, write=write)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@register_action(
|
|
160
|
+
action_id="fix.ids",
|
|
161
|
+
description="Add missing ID tags to functions and classes",
|
|
162
|
+
category=ActionCategory.FIX,
|
|
163
|
+
policies=["rules/code/purity"],
|
|
164
|
+
remediates=["purity.stable_id_anchor", "linkage.assign_ids"],
|
|
165
|
+
)
|
|
166
|
+
@atomic_action(
|
|
167
|
+
action_id="fix.ids",
|
|
168
|
+
intent="Atomic action for action_fix_ids",
|
|
169
|
+
impact=ActionImpact.WRITE_CODE,
|
|
170
|
+
policies=["atomic_actions"],
|
|
171
|
+
)
|
|
172
|
+
# ID: 854f5010-aaec-4f14-8d72-3e3070334c54
|
|
173
|
+
async def action_fix_ids(
|
|
174
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
175
|
+
) -> ActionResult:
|
|
176
|
+
"""Fix missing ID tags."""
|
|
177
|
+
from body.self_healing.id_tagging_service import fix_ids_internal
|
|
178
|
+
|
|
179
|
+
return await fix_ids_internal(core_context, write=write)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@register_action(
|
|
183
|
+
action_id="fix.duplicate_ids",
|
|
184
|
+
description="Resolve duplicate UUID collisions across source files",
|
|
185
|
+
category=ActionCategory.FIX,
|
|
186
|
+
policies=["rules/code/linkage"],
|
|
187
|
+
remediates=["linkage.duplicate_ids"],
|
|
188
|
+
)
|
|
189
|
+
@atomic_action(
|
|
190
|
+
action_id="fix.duplicate_ids",
|
|
191
|
+
intent="Resolve duplicate ID conflicts by regenerating UUIDs",
|
|
192
|
+
impact=ActionImpact.WRITE_METADATA,
|
|
193
|
+
policies=["id_uniqueness_check"],
|
|
194
|
+
)
|
|
195
|
+
# ID: 2e7f8a1b-c3d4-4e5f-96a0-b1d2e3f4a5b6
|
|
196
|
+
async def action_fix_duplicate_ids(
|
|
197
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
198
|
+
) -> ActionResult:
|
|
199
|
+
"""Resolve duplicate UUID collisions in source files."""
|
|
200
|
+
from body.self_healing.duplicate_id_service import fix_duplicate_ids_internal
|
|
201
|
+
|
|
202
|
+
return await fix_duplicate_ids_internal(core_context, write=write)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@register_action(
|
|
206
|
+
action_id="fix.logging",
|
|
207
|
+
description="Replace print statements with proper logging",
|
|
208
|
+
category=ActionCategory.FIX,
|
|
209
|
+
policies=["rules/code/purity"],
|
|
210
|
+
remediates=["logic.logging.standard_only"],
|
|
211
|
+
)
|
|
212
|
+
@atomic_action(
|
|
213
|
+
action_id="fix.logging",
|
|
214
|
+
intent="Atomic action for action_fix_logging",
|
|
215
|
+
impact=ActionImpact.WRITE_CODE,
|
|
216
|
+
policies=["atomic_actions"],
|
|
217
|
+
)
|
|
218
|
+
# ID: 7661f20f-3d41-4501-a0ab-c30804d29ad0
|
|
219
|
+
async def action_fix_logging(
|
|
220
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
221
|
+
) -> ActionResult:
|
|
222
|
+
"""Fix logging violations."""
|
|
223
|
+
start = time.time()
|
|
224
|
+
from body.self_healing.logging_service import LoggingFixer
|
|
225
|
+
|
|
226
|
+
if core_context.file_handler is None:
|
|
227
|
+
return ActionResult(
|
|
228
|
+
action_id="fix.logging",
|
|
229
|
+
ok=False,
|
|
230
|
+
data={"error": "file_handler not initialized"},
|
|
231
|
+
duration_sec=time.time() - start,
|
|
232
|
+
)
|
|
233
|
+
fixer = LoggingFixer(
|
|
234
|
+
repo_root=core_context.git_service.repo_path,
|
|
235
|
+
file_handler=core_context.file_handler,
|
|
236
|
+
dry_run=not write,
|
|
237
|
+
)
|
|
238
|
+
try:
|
|
239
|
+
result = fixer.fix_all()
|
|
240
|
+
except Exception as e:
|
|
241
|
+
return ActionResult(
|
|
242
|
+
action_id="fix.logging",
|
|
243
|
+
ok=False,
|
|
244
|
+
data={"error": str(e)},
|
|
245
|
+
duration_sec=time.time() - start,
|
|
246
|
+
)
|
|
247
|
+
return ActionResult(
|
|
248
|
+
action_id="fix.logging",
|
|
249
|
+
ok=True,
|
|
250
|
+
data=result,
|
|
251
|
+
duration_sec=time.time() - start,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@register_action(
|
|
256
|
+
action_id="fix.placeholders",
|
|
257
|
+
description="Replace FUTURE/PENDING placeholders",
|
|
258
|
+
category=ActionCategory.FIX,
|
|
259
|
+
policies=["rules/code/purity"],
|
|
260
|
+
remediates=["caps.no_placeholder_text"],
|
|
261
|
+
)
|
|
262
|
+
@atomic_action(
|
|
263
|
+
action_id="fix.placeholders",
|
|
264
|
+
intent="Atomic action for action_fix_placeholders",
|
|
265
|
+
impact=ActionImpact.WRITE_CODE,
|
|
266
|
+
policies=["atomic_actions"],
|
|
267
|
+
)
|
|
268
|
+
# ID: fc997ae2-05e6-4823-81dd-e645afee2a7e
|
|
269
|
+
async def action_fix_placeholders(
|
|
270
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
271
|
+
) -> ActionResult:
|
|
272
|
+
"""Replace placeholder comments.
|
|
273
|
+
|
|
274
|
+
Two invocation modes:
|
|
275
|
+
|
|
276
|
+
1. Targeted (autonomous loop): caller supplies ``file_path`` in kwargs
|
|
277
|
+
(ViolationRemediatorWorker threads this through ``ProposalAction.parameters``).
|
|
278
|
+
The action operates on exactly that file. This matches the action's
|
|
279
|
+
``"safe"`` classification — bounded scope per invocation.
|
|
280
|
+
|
|
281
|
+
2. Sweep (legacy CLI): no ``file_path`` supplied. The action walks every
|
|
282
|
+
``*.py`` under ``src/``. This mode is preserved for backwards
|
|
283
|
+
compatibility with ``cli.commands.fix_placeholders`` but emits a
|
|
284
|
+
warning so any remaining unbounded autonomous callers are visible.
|
|
285
|
+
|
|
286
|
+
On IntentGuard rejection the handler uses ``_error_data`` to persist the
|
|
287
|
+
structured ``ConstitutionalViolationError.to_dict()`` payload — ``rule_name``,
|
|
288
|
+
``path``, ``source_policy`` survive into ``proposal.execution_results``
|
|
289
|
+
instead of collapsing to a flat error string.
|
|
290
|
+
"""
|
|
291
|
+
start = time.time()
|
|
292
|
+
from body.self_healing.placeholder_fixer_service import (
|
|
293
|
+
fix_placeholders_in_content,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
repo_root: Path = core_context.git_service.repo_path
|
|
297
|
+
file_path = kwargs.get("file_path")
|
|
298
|
+
|
|
299
|
+
# ---- Targeted mode ------------------------------------------------------
|
|
300
|
+
if file_path:
|
|
301
|
+
try:
|
|
302
|
+
target_rel = str(file_path).lstrip("./")
|
|
303
|
+
target_abs = (repo_root / target_rel).resolve()
|
|
304
|
+
|
|
305
|
+
# Keep the action bounded to src/ — refuse anything outside it.
|
|
306
|
+
src_root = (repo_root / "src").resolve()
|
|
307
|
+
try:
|
|
308
|
+
target_abs.relative_to(src_root)
|
|
309
|
+
except ValueError:
|
|
310
|
+
return ActionResult(
|
|
311
|
+
action_id="fix.placeholders",
|
|
312
|
+
ok=False,
|
|
313
|
+
data={
|
|
314
|
+
"error": f"Target outside src/ scope: {target_rel}",
|
|
315
|
+
"file_path": target_rel,
|
|
316
|
+
},
|
|
317
|
+
duration_sec=time.time() - start,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
if not target_abs.is_file() or target_abs.suffix != ".py":
|
|
321
|
+
return ActionResult(
|
|
322
|
+
action_id="fix.placeholders",
|
|
323
|
+
ok=False,
|
|
324
|
+
data={
|
|
325
|
+
"error": f"Target is not a .py file: {target_rel}",
|
|
326
|
+
"file_path": target_rel,
|
|
327
|
+
},
|
|
328
|
+
duration_sec=time.time() - start,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
original = target_abs.read_text(encoding="utf-8")
|
|
332
|
+
fixed = fix_placeholders_in_content(original)
|
|
333
|
+
|
|
334
|
+
if fixed == original:
|
|
335
|
+
return ActionResult(
|
|
336
|
+
action_id="fix.placeholders",
|
|
337
|
+
ok=True,
|
|
338
|
+
data={
|
|
339
|
+
"files_affected": 0,
|
|
340
|
+
"written": False,
|
|
341
|
+
"file_path": target_rel,
|
|
342
|
+
"note": "no placeholders found",
|
|
343
|
+
},
|
|
344
|
+
duration_sec=time.time() - start,
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
if write:
|
|
348
|
+
core_context.file_handler.write_runtime_text(target_rel, fixed)
|
|
349
|
+
|
|
350
|
+
return ActionResult(
|
|
351
|
+
action_id="fix.placeholders",
|
|
352
|
+
ok=True,
|
|
353
|
+
data={
|
|
354
|
+
"files_affected": 1,
|
|
355
|
+
"written": write,
|
|
356
|
+
"file_path": target_rel,
|
|
357
|
+
},
|
|
358
|
+
duration_sec=time.time() - start,
|
|
359
|
+
)
|
|
360
|
+
except Exception as e:
|
|
361
|
+
return ActionResult(
|
|
362
|
+
action_id="fix.placeholders",
|
|
363
|
+
ok=False,
|
|
364
|
+
data=_error_data(e, file_path=str(file_path)),
|
|
365
|
+
duration_sec=time.time() - start,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# ---- Sweep mode (CLI callers only) -------------------------------------
|
|
369
|
+
logger.warning(
|
|
370
|
+
"fix.placeholders invoked in sweep mode (no file_path). "
|
|
371
|
+
"This mode is reserved for CLI callers; autonomous callers MUST "
|
|
372
|
+
"supply file_path to stay within their declared impact scope."
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
files_modified = 0
|
|
376
|
+
try:
|
|
377
|
+
src_dir = repo_root / "src"
|
|
378
|
+
for py_file in src_dir.rglob("*.py"):
|
|
379
|
+
original = py_file.read_text(encoding="utf-8")
|
|
380
|
+
fixed = fix_placeholders_in_content(original)
|
|
381
|
+
if fixed != original:
|
|
382
|
+
if write:
|
|
383
|
+
rel_path = str(py_file.relative_to(repo_root))
|
|
384
|
+
core_context.file_handler.write_runtime_text(rel_path, fixed)
|
|
385
|
+
files_modified += 1
|
|
386
|
+
return ActionResult(
|
|
387
|
+
action_id="fix.placeholders",
|
|
388
|
+
ok=True,
|
|
389
|
+
data={"files_affected": files_modified, "written": write, "mode": "sweep"},
|
|
390
|
+
duration_sec=time.time() - start,
|
|
391
|
+
)
|
|
392
|
+
except Exception as e:
|
|
393
|
+
return ActionResult(
|
|
394
|
+
action_id="fix.placeholders",
|
|
395
|
+
ok=False,
|
|
396
|
+
data=_error_data(e, mode="sweep"),
|
|
397
|
+
duration_sec=time.time() - start,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@register_action(
|
|
402
|
+
action_id="fix.atomic_actions",
|
|
403
|
+
description="Fix atomic actions pattern violations",
|
|
404
|
+
category=ActionCategory.FIX,
|
|
405
|
+
policies=["rules/architecture/atomic_actions"],
|
|
406
|
+
remediates=["architecture.atomic_actions.must_return_action_result"],
|
|
407
|
+
)
|
|
408
|
+
@atomic_action(
|
|
409
|
+
action_id="fix.atomic_actions",
|
|
410
|
+
intent="Atomic action for action_fix_atomic_actions",
|
|
411
|
+
impact=ActionImpact.WRITE_CODE,
|
|
412
|
+
policies=["atomic_actions"],
|
|
413
|
+
)
|
|
414
|
+
# ID: 525fff82-3c87-4847-83a5-346ad9c78534
|
|
415
|
+
async def action_fix_atomic_actions(
|
|
416
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
417
|
+
) -> ActionResult:
|
|
418
|
+
"""Fix atomic action patterns."""
|
|
419
|
+
from body.evaluators.atomic_actions_evaluator import AtomicActionsEvaluator
|
|
420
|
+
from body.evaluators.atomic_actions_rules import AtomicActionViolation
|
|
421
|
+
from body.self_healing.atomic_actions_fixer import fix_file_violations
|
|
422
|
+
|
|
423
|
+
start_time = time.time()
|
|
424
|
+
root_path = core_context.git_service.repo_path
|
|
425
|
+
|
|
426
|
+
evaluator = AtomicActionsEvaluator(context=core_context)
|
|
427
|
+
try:
|
|
428
|
+
result_wrapper = await evaluator.execute(repo_root=root_path)
|
|
429
|
+
data = result_wrapper.data
|
|
430
|
+
except Exception as e:
|
|
431
|
+
return ActionResult(
|
|
432
|
+
action_id="fix.atomic_actions",
|
|
433
|
+
ok=False,
|
|
434
|
+
data={"error": str(e)},
|
|
435
|
+
duration_sec=time.time() - start_time,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
if not data["violations"]:
|
|
439
|
+
return ActionResult(
|
|
440
|
+
action_id="fix.atomic_actions", ok=True, data={"violations_fixed": 0}
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
violations = [
|
|
444
|
+
AtomicActionViolation(
|
|
445
|
+
file_path=root_path / v["file"],
|
|
446
|
+
function_name=v["function"],
|
|
447
|
+
rule_id=v["rule"],
|
|
448
|
+
message=v["message"],
|
|
449
|
+
severity=v["severity"],
|
|
450
|
+
line_number=v["line"],
|
|
451
|
+
suggested_fix=v.get("suggested_fix"),
|
|
452
|
+
)
|
|
453
|
+
for v in data["violations"]
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
violations_by_file = {}
|
|
457
|
+
for v in violations:
|
|
458
|
+
violations_by_file.setdefault(v.file_path, []).append(v)
|
|
459
|
+
|
|
460
|
+
fixes_applied = 0
|
|
461
|
+
files_modified = 0
|
|
462
|
+
files_failed = 0
|
|
463
|
+
|
|
464
|
+
for file_path, file_violations in violations_by_file.items():
|
|
465
|
+
try:
|
|
466
|
+
source = file_path.read_text(encoding="utf-8")
|
|
467
|
+
modified_source = fix_file_violations(source, file_violations, file_path)
|
|
468
|
+
if modified_source != source:
|
|
469
|
+
if write:
|
|
470
|
+
rel_path = str(file_path.relative_to(root_path))
|
|
471
|
+
core_context.file_handler.write_runtime_text(
|
|
472
|
+
rel_path, modified_source
|
|
473
|
+
)
|
|
474
|
+
files_modified += 1
|
|
475
|
+
fixes_applied += len(file_violations)
|
|
476
|
+
except Exception as e:
|
|
477
|
+
logger.error("Error fixing %s: %s", file_path, e)
|
|
478
|
+
files_failed += 1
|
|
479
|
+
|
|
480
|
+
return ActionResult(
|
|
481
|
+
action_id="fix.atomic_actions",
|
|
482
|
+
ok=files_failed == 0,
|
|
483
|
+
data={
|
|
484
|
+
"files_modified": files_modified,
|
|
485
|
+
"violations_fixed": fixes_applied,
|
|
486
|
+
"files_failed": files_failed,
|
|
487
|
+
},
|
|
488
|
+
duration_sec=time.time() - start_time,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@register_action(
|
|
493
|
+
action_id="fix.docstrings",
|
|
494
|
+
description="Generate and inject missing docstrings using AI",
|
|
495
|
+
category=ActionCategory.FIX,
|
|
496
|
+
policies=["rules/code/purity"],
|
|
497
|
+
remediates=["purity.docstrings.required"],
|
|
498
|
+
)
|
|
499
|
+
@atomic_action(
|
|
500
|
+
action_id="fix.docstrings",
|
|
501
|
+
intent="Autonomously generate missing docstrings via Coder LLM role",
|
|
502
|
+
impact=ActionImpact.WRITE_CODE,
|
|
503
|
+
policies=["atomic_actions"],
|
|
504
|
+
)
|
|
505
|
+
# ID: a3f91c7d-5e2b-4d8a-b6f0-c1e2d3f4a5b7
|
|
506
|
+
async def action_fix_docstrings(
|
|
507
|
+
core_context: CoreContext,
|
|
508
|
+
file_path: str | None = None,
|
|
509
|
+
write: bool = False,
|
|
510
|
+
limit: int = 0,
|
|
511
|
+
**kwargs,
|
|
512
|
+
) -> ActionResult:
|
|
513
|
+
"""Generate and inject missing docstrings using the Coder LLM role.
|
|
514
|
+
|
|
515
|
+
Two invocation modes:
|
|
516
|
+
|
|
517
|
+
1. Targeted (autonomous loop): caller supplies ``file_path`` in kwargs,
|
|
518
|
+
e.g. via ProposalExecutor expanding ``actions[i].parameters.file_path``.
|
|
519
|
+
Only symbols inside that file are evaluated. Mirrors the pattern
|
|
520
|
+
used by ``action_format_code`` and ``action_fix_modularity``.
|
|
521
|
+
2. Sweep (legacy CLI): no ``file_path`` supplied. The action walks every
|
|
522
|
+
symbol in the knowledge graph, as before. ``limit`` caps the symbol
|
|
523
|
+
count in sweep mode; 0 means unlimited and is ignored when
|
|
524
|
+
``file_path`` is set (matches body's fix_docstrings contract).
|
|
525
|
+
|
|
526
|
+
Before this change the action discarded ``**kwargs``, so every targeted
|
|
527
|
+
proposal silently degraded to a full-tree sweep — one proposal hammered
|
|
528
|
+
Ollama for the whole codebase regardless of its declared scope.
|
|
529
|
+
"""
|
|
530
|
+
start = time.time()
|
|
531
|
+
from body.self_healing.docstring_service import fix_docstrings
|
|
532
|
+
|
|
533
|
+
try:
|
|
534
|
+
await fix_docstrings(
|
|
535
|
+
context=core_context, write=write, limit=limit, file_path=file_path
|
|
536
|
+
)
|
|
537
|
+
except Exception as e:
|
|
538
|
+
return ActionResult(
|
|
539
|
+
action_id="fix.docstrings",
|
|
540
|
+
ok=False,
|
|
541
|
+
data={
|
|
542
|
+
"error": str(e),
|
|
543
|
+
"write": write,
|
|
544
|
+
"file_path": file_path,
|
|
545
|
+
"limit": limit,
|
|
546
|
+
},
|
|
547
|
+
duration_sec=time.time() - start,
|
|
548
|
+
)
|
|
549
|
+
return ActionResult(
|
|
550
|
+
action_id="fix.docstrings",
|
|
551
|
+
ok=True,
|
|
552
|
+
data={"write": write, "file_path": file_path, "limit": limit},
|
|
553
|
+
duration_sec=time.time() - start,
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@register_action(
|
|
558
|
+
action_id="fix.purge_legacy_tags",
|
|
559
|
+
description="Remove obsolete tag formats (e.g. legacy '# owner:', '# Tag:' lines)",
|
|
560
|
+
category=ActionCategory.FIX,
|
|
561
|
+
policies=["tag_hygiene"],
|
|
562
|
+
remediates=["metadata.no_legacy_tags"],
|
|
563
|
+
)
|
|
564
|
+
@atomic_action(
|
|
565
|
+
action_id="fix.purge_legacy_tags",
|
|
566
|
+
intent="Remove legacy tag formats from source files",
|
|
567
|
+
impact=ActionImpact.WRITE_METADATA,
|
|
568
|
+
policies=["atomic_actions"],
|
|
569
|
+
)
|
|
570
|
+
# ID: 3c8d2f15-7e94-4a6b-b1c5-9d3f7e2a4b8d
|
|
571
|
+
async def action_fix_purge_legacy_tags(
|
|
572
|
+
core_context: CoreContext,
|
|
573
|
+
write: bool = False,
|
|
574
|
+
**kwargs,
|
|
575
|
+
) -> ActionResult:
|
|
576
|
+
"""Wrap body.self_healing.purge_legacy_tags_service.purge_legacy_tags."""
|
|
577
|
+
start = time.time()
|
|
578
|
+
from body.self_healing.purge_legacy_tags_service import purge_legacy_tags
|
|
579
|
+
|
|
580
|
+
try:
|
|
581
|
+
removed = await purge_legacy_tags(core_context, dry_run=not write)
|
|
582
|
+
except Exception as e:
|
|
583
|
+
return ActionResult(
|
|
584
|
+
action_id="fix.purge_legacy_tags",
|
|
585
|
+
ok=False,
|
|
586
|
+
data={"error": str(e), "write": write},
|
|
587
|
+
duration_sec=time.time() - start,
|
|
588
|
+
)
|
|
589
|
+
return ActionResult(
|
|
590
|
+
action_id="fix.purge_legacy_tags",
|
|
591
|
+
ok=True,
|
|
592
|
+
data={"removed": removed, "write": write},
|
|
593
|
+
duration_sec=time.time() - start,
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
@register_action(
|
|
598
|
+
action_id="fix.capability_tagging",
|
|
599
|
+
description="Tag untagged public capabilities using LLM-suggested names",
|
|
600
|
+
category=ActionCategory.FIX,
|
|
601
|
+
policies=["capability_tagging"],
|
|
602
|
+
remediates=["symbols.capability_required"],
|
|
603
|
+
)
|
|
604
|
+
@atomic_action(
|
|
605
|
+
action_id="fix.capability_tagging",
|
|
606
|
+
intent="Tag untagged capabilities via LLM",
|
|
607
|
+
impact=ActionImpact.WRITE_METADATA,
|
|
608
|
+
policies=["atomic_actions"],
|
|
609
|
+
)
|
|
610
|
+
# ID: 7b3c8d51-9e2a-4f7b-a1e6-5c4d8b2f9a3e
|
|
611
|
+
async def action_fix_capability_tagging(
|
|
612
|
+
core_context: CoreContext,
|
|
613
|
+
write: bool = False,
|
|
614
|
+
limit: int = 0,
|
|
615
|
+
**kwargs,
|
|
616
|
+
) -> ActionResult:
|
|
617
|
+
"""Tag untagged capabilities via the will-layer naming agent.
|
|
618
|
+
|
|
619
|
+
Wraps will.self_healing.capability_tagging_service.main_async. The
|
|
620
|
+
body→will lazy import matches the precedent in
|
|
621
|
+
proposal_lifecycle_actions.py — capability_tagging's CapabilityTaggerAgent
|
|
622
|
+
legitimately depends on will/orchestration, so it stays in will/.
|
|
623
|
+
"""
|
|
624
|
+
start = time.time()
|
|
625
|
+
from shared.infrastructure.database.session_manager import get_session
|
|
626
|
+
from will.self_healing.capability_tagging_service import main_async as tag
|
|
627
|
+
|
|
628
|
+
try:
|
|
629
|
+
await tag(
|
|
630
|
+
session_factory=get_session,
|
|
631
|
+
cognitive_service=core_context.cognitive_service,
|
|
632
|
+
knowledge_service=core_context.knowledge_service,
|
|
633
|
+
write=write,
|
|
634
|
+
dry_run=not write,
|
|
635
|
+
limit=limit,
|
|
636
|
+
)
|
|
637
|
+
except Exception as e:
|
|
638
|
+
return ActionResult(
|
|
639
|
+
action_id="fix.capability_tagging",
|
|
640
|
+
ok=False,
|
|
641
|
+
data={"error": str(e), "write": write, "limit": limit},
|
|
642
|
+
duration_sec=time.time() - start,
|
|
643
|
+
)
|
|
644
|
+
return ActionResult(
|
|
645
|
+
action_id="fix.capability_tagging",
|
|
646
|
+
ok=True,
|
|
647
|
+
data={"write": write, "limit": limit},
|
|
648
|
+
duration_sec=time.time() - start,
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
@register_action(
|
|
653
|
+
action_id="fix.vulture_heal",
|
|
654
|
+
description="Surgically remove Vulture-identified dead code using LLM analysis",
|
|
655
|
+
category=ActionCategory.FIX,
|
|
656
|
+
policies=["dead_code"],
|
|
657
|
+
remediates=["workflow.dead_code_check"],
|
|
658
|
+
)
|
|
659
|
+
@atomic_action(
|
|
660
|
+
action_id="fix.vulture_heal",
|
|
661
|
+
intent="Remove dead code findings via LLM",
|
|
662
|
+
impact=ActionImpact.WRITE_CODE,
|
|
663
|
+
policies=["atomic_actions"],
|
|
664
|
+
)
|
|
665
|
+
# ID: 5a2e9c4f-1b8d-43a7-92e6-7f3c1d4b8e5a
|
|
666
|
+
async def action_fix_vulture_heal(
|
|
667
|
+
core_context: CoreContext,
|
|
668
|
+
write: bool = False,
|
|
669
|
+
**kwargs,
|
|
670
|
+
) -> ActionResult:
|
|
671
|
+
"""Surgical dead-code cleanup via the relocated body.self_healing.vulture_healer."""
|
|
672
|
+
start = time.time()
|
|
673
|
+
from body.self_healing.vulture_healer import heal_dead_code
|
|
674
|
+
|
|
675
|
+
try:
|
|
676
|
+
await heal_dead_code(
|
|
677
|
+
context=core_context,
|
|
678
|
+
file_handler=core_context.file_handler,
|
|
679
|
+
repo_root=core_context.git_service.repo_path,
|
|
680
|
+
write=write,
|
|
681
|
+
)
|
|
682
|
+
except Exception as e:
|
|
683
|
+
return ActionResult(
|
|
684
|
+
action_id="fix.vulture_heal",
|
|
685
|
+
ok=False,
|
|
686
|
+
data={"error": str(e), "write": write},
|
|
687
|
+
duration_sec=time.time() - start,
|
|
688
|
+
)
|
|
689
|
+
return ActionResult(
|
|
690
|
+
action_id="fix.vulture_heal",
|
|
691
|
+
ok=True,
|
|
692
|
+
data={"write": write},
|
|
693
|
+
duration_sec=time.time() - start,
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
@register_action(
|
|
698
|
+
action_id="fix.settings_access",
|
|
699
|
+
description="Refactor settings.* imports to dependency injection via CoreContext",
|
|
700
|
+
category=ActionCategory.FIX,
|
|
701
|
+
policies=["dependency_injection"],
|
|
702
|
+
remediates=["architecture.body.no_settings_import"],
|
|
703
|
+
)
|
|
704
|
+
@atomic_action(
|
|
705
|
+
action_id="fix.settings_access",
|
|
706
|
+
intent="Refactor settings imports to DI via CoreContext",
|
|
707
|
+
impact=ActionImpact.WRITE_CODE,
|
|
708
|
+
policies=["atomic_actions"],
|
|
709
|
+
)
|
|
710
|
+
# ID: 9e8d2c1f-3a6b-4d57-b9e2-1f4a8c5d3e0b
|
|
711
|
+
async def action_fix_settings_access(
|
|
712
|
+
core_context: CoreContext,
|
|
713
|
+
write: bool = False,
|
|
714
|
+
layers: list[str] | None = None,
|
|
715
|
+
**kwargs,
|
|
716
|
+
) -> ActionResult:
|
|
717
|
+
"""Refactor settings imports to dependency injection across given layers.
|
|
718
|
+
|
|
719
|
+
Wraps body.maintenance.refactor_settings_access; translates the
|
|
720
|
+
atomic-action convention (`write`) to the wrapped function's
|
|
721
|
+
(`dry_run = not write`), passes repo_path from CoreContext, and
|
|
722
|
+
surfaces the per-layer summary on data.results.
|
|
723
|
+
"""
|
|
724
|
+
start = time.time()
|
|
725
|
+
from body.maintenance.refactor_settings_access import refactor_settings_access
|
|
726
|
+
|
|
727
|
+
try:
|
|
728
|
+
results = await refactor_settings_access(
|
|
729
|
+
repo_path=core_context.git_service.repo_path,
|
|
730
|
+
layers=layers,
|
|
731
|
+
dry_run=not write,
|
|
732
|
+
)
|
|
733
|
+
except Exception as e:
|
|
734
|
+
return ActionResult(
|
|
735
|
+
action_id="fix.settings_access",
|
|
736
|
+
ok=False,
|
|
737
|
+
data={"error": str(e), "write": write, "layers": layers},
|
|
738
|
+
duration_sec=time.time() - start,
|
|
739
|
+
)
|
|
740
|
+
return ActionResult(
|
|
741
|
+
action_id="fix.settings_access",
|
|
742
|
+
ok=True,
|
|
743
|
+
data={"results": results, "write": write, "layers": layers},
|
|
744
|
+
duration_sec=time.time() - start,
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
_PATH_RESOLVER_PROPS: dict[str, str] = {"reports": "reports_dir", "logs": "logs_dir"}
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def _is_target_constant(node: Any) -> bool:
|
|
752
|
+
import ast as _ast
|
|
753
|
+
|
|
754
|
+
return (
|
|
755
|
+
isinstance(node, _ast.Constant)
|
|
756
|
+
and isinstance(node.value, str)
|
|
757
|
+
and node.value in _PATH_RESOLVER_PROPS
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def _is_embedded_target_constant(node: Any) -> tuple[bool, str, str]:
|
|
762
|
+
"""Detect a Constant str whose value starts with ``reports/`` or ``logs/``.
|
|
763
|
+
|
|
764
|
+
Returns ``(matched, prop_name, remainder)`` where ``prop_name`` is the
|
|
765
|
+
matching PathResolver property (``reports_dir``/``logs_dir``) and
|
|
766
|
+
``remainder`` is the substring after the prefix-and-slash. ``remainder``
|
|
767
|
+
may be empty for bare cases where only the prefix-and-slash is present.
|
|
768
|
+
"""
|
|
769
|
+
import ast as _ast
|
|
770
|
+
|
|
771
|
+
if not (isinstance(node, _ast.Constant) and isinstance(node.value, str)):
|
|
772
|
+
return False, "", ""
|
|
773
|
+
for prefix, prop_name in _PATH_RESOLVER_PROPS.items():
|
|
774
|
+
head = f"{prefix}/"
|
|
775
|
+
if node.value.startswith(head):
|
|
776
|
+
return True, prop_name, node.value[len(head) :]
|
|
777
|
+
return False, "", ""
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def _has_target_descendant(node: Any) -> bool:
|
|
781
|
+
"""True if `node` or any descendant BinOp has a matching constant operand.
|
|
782
|
+
|
|
783
|
+
Used to skip outer BinOps in chained path-division expressions where the
|
|
784
|
+
runtime directory name appears as an intermediate operand. Detects both
|
|
785
|
+
leaf-target forms and embedded-target forms (where the runtime directory
|
|
786
|
+
name is the prefix of a longer Constant) so nested rewrites do not overlap.
|
|
787
|
+
"""
|
|
788
|
+
import ast as _ast
|
|
789
|
+
|
|
790
|
+
for sub in _ast.walk(node):
|
|
791
|
+
if isinstance(sub, _ast.BinOp) and isinstance(sub.op, _ast.Div):
|
|
792
|
+
if _is_target_constant(sub.left) or _is_target_constant(sub.right):
|
|
793
|
+
return True
|
|
794
|
+
if _is_embedded_target_constant(sub.right)[0]:
|
|
795
|
+
return True
|
|
796
|
+
return False
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def _insert_path_resolver_import(source: str) -> str:
|
|
800
|
+
"""Insert ``from shared.path_resolver import PathResolver`` into the module.
|
|
801
|
+
|
|
802
|
+
Idempotent — returns `source` unchanged if the import is already present.
|
|
803
|
+
Inserts in alphabetically correct position within the existing first-party
|
|
804
|
+
import block (CORE roots: api, body, cli, mind, shared, will). Falls back
|
|
805
|
+
to appending after the last top-level Import/ImportFrom, or after a module
|
|
806
|
+
docstring if no imports exist.
|
|
807
|
+
"""
|
|
808
|
+
import ast as _ast
|
|
809
|
+
|
|
810
|
+
import_line = "from shared.path_resolver import PathResolver"
|
|
811
|
+
if import_line in source:
|
|
812
|
+
return source
|
|
813
|
+
|
|
814
|
+
try:
|
|
815
|
+
import asttokens as _asttokens
|
|
816
|
+
|
|
817
|
+
atok = _asttokens.ASTTokens(source, parse=True)
|
|
818
|
+
except (SyntaxError, ImportError):
|
|
819
|
+
return source
|
|
820
|
+
tree = atok.tree
|
|
821
|
+
|
|
822
|
+
new_module = "shared.path_resolver"
|
|
823
|
+
first_party_roots = {"api", "body", "cli", "mind", "shared", "will"}
|
|
824
|
+
|
|
825
|
+
# Absolute first-party ImportFrom siblings — same isort group as the new
|
|
826
|
+
# import. Relative imports (level > 0) are excluded; they form a separate
|
|
827
|
+
# group below first-party.
|
|
828
|
+
siblings: list[Any] = []
|
|
829
|
+
for node in tree.body:
|
|
830
|
+
if (
|
|
831
|
+
isinstance(node, _ast.ImportFrom)
|
|
832
|
+
and node.module
|
|
833
|
+
and node.level == 0
|
|
834
|
+
and node.module.split(".")[0] in first_party_roots
|
|
835
|
+
):
|
|
836
|
+
siblings.append(node)
|
|
837
|
+
|
|
838
|
+
if siblings:
|
|
839
|
+
insert_after = None
|
|
840
|
+
insert_before = None
|
|
841
|
+
for node in siblings:
|
|
842
|
+
if node.module < new_module:
|
|
843
|
+
insert_after = node
|
|
844
|
+
else:
|
|
845
|
+
insert_before = node
|
|
846
|
+
break
|
|
847
|
+
if insert_after is not None:
|
|
848
|
+
_, end = atok.get_text_range(insert_after)
|
|
849
|
+
return source[:end] + "\n" + import_line + source[end:]
|
|
850
|
+
if insert_before is not None:
|
|
851
|
+
start, _ = atok.get_text_range(insert_before)
|
|
852
|
+
return source[:start] + import_line + "\n" + source[start:]
|
|
853
|
+
|
|
854
|
+
# No first-party siblings — fall back to appending after the last import.
|
|
855
|
+
last_import = None
|
|
856
|
+
for node in tree.body:
|
|
857
|
+
if isinstance(node, (_ast.Import, _ast.ImportFrom)):
|
|
858
|
+
last_import = node
|
|
859
|
+
|
|
860
|
+
if last_import is not None:
|
|
861
|
+
_, end = atok.get_text_range(last_import)
|
|
862
|
+
return source[:end] + "\n" + import_line + source[end:]
|
|
863
|
+
|
|
864
|
+
# No imports: insert after module docstring if present, else at top.
|
|
865
|
+
insert_at = 0
|
|
866
|
+
if (
|
|
867
|
+
tree.body
|
|
868
|
+
and isinstance(tree.body[0], _ast.Expr)
|
|
869
|
+
and isinstance(tree.body[0].value, _ast.Constant)
|
|
870
|
+
and isinstance(tree.body[0].value.value, str)
|
|
871
|
+
):
|
|
872
|
+
_, insert_at = atok.get_text_range(tree.body[0])
|
|
873
|
+
|
|
874
|
+
return source[:insert_at] + "\n\n" + import_line + "\n" + source[insert_at:]
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
def _transform_path_resolver(source: str) -> tuple[str, int]:
|
|
878
|
+
"""Deterministic AST rewrite of hardcoded runtime dir literals.
|
|
879
|
+
|
|
880
|
+
Walks the source for ``BinOp(op=Div)`` nodes where one operand is a
|
|
881
|
+
``Constant("reports")`` or ``Constant("logs")``. Each leaf match is
|
|
882
|
+
replaced with ``PathResolver.from_repo(<other operand>).<dir>`` where
|
|
883
|
+
``<dir>`` is ``reports_dir`` or ``logs_dir`` respectively. Outer BinOps
|
|
884
|
+
that contain a deeper match are skipped (leaf-only strategy) so source
|
|
885
|
+
edits never overlap. Chained path-division expressions with a runtime
|
|
886
|
+
directory name as an intermediate operand are handled by the leaf
|
|
887
|
+
rewrite alone.
|
|
888
|
+
|
|
889
|
+
Returns ``(new_source, replacement_count)``. If the source fails to
|
|
890
|
+
parse, returns ``(source, 0)``.
|
|
891
|
+
"""
|
|
892
|
+
import ast as _ast
|
|
893
|
+
|
|
894
|
+
try:
|
|
895
|
+
import asttokens as _asttokens
|
|
896
|
+
|
|
897
|
+
atok = _asttokens.ASTTokens(source, parse=True)
|
|
898
|
+
except (SyntaxError, ImportError):
|
|
899
|
+
return source, 0
|
|
900
|
+
tree = atok.tree
|
|
901
|
+
|
|
902
|
+
replacements: list[tuple[int, int, str]] = []
|
|
903
|
+
for node in _ast.walk(tree):
|
|
904
|
+
if not (isinstance(node, _ast.BinOp) and isinstance(node.op, _ast.Div)):
|
|
905
|
+
continue
|
|
906
|
+
|
|
907
|
+
# Form 0 — leaf target: runtime directory name as a standalone Constant operand.
|
|
908
|
+
if _is_target_constant(node.right):
|
|
909
|
+
target, other = node.right, node.left
|
|
910
|
+
prop_name = _PATH_RESOLVER_PROPS[target.value]
|
|
911
|
+
remainder = ""
|
|
912
|
+
elif _is_target_constant(node.left):
|
|
913
|
+
target, other = node.left, node.right
|
|
914
|
+
prop_name = _PATH_RESOLVER_PROPS[target.value]
|
|
915
|
+
remainder = ""
|
|
916
|
+
else:
|
|
917
|
+
# Form 1 — embedded target: runtime directory name as the prefix of
|
|
918
|
+
# a longer Constant on the right operand.
|
|
919
|
+
matched, prop_name, remainder = _is_embedded_target_constant(node.right)
|
|
920
|
+
if not matched:
|
|
921
|
+
continue
|
|
922
|
+
other = node.left
|
|
923
|
+
|
|
924
|
+
if _has_target_descendant(other):
|
|
925
|
+
continue
|
|
926
|
+
|
|
927
|
+
other_text = atok.get_text(other)
|
|
928
|
+
base = f"PathResolver.from_repo({other_text}).{prop_name}"
|
|
929
|
+
replacement_text = f'{base} / "{remainder}"' if remainder else base
|
|
930
|
+
start, end = atok.get_text_range(node)
|
|
931
|
+
replacements.append((start, end, replacement_text))
|
|
932
|
+
|
|
933
|
+
if not replacements:
|
|
934
|
+
return source, 0
|
|
935
|
+
|
|
936
|
+
replacements.sort(key=lambda r: r[0], reverse=True)
|
|
937
|
+
new_source = source
|
|
938
|
+
for start, end, text in replacements:
|
|
939
|
+
new_source = new_source[:start] + text + new_source[end:]
|
|
940
|
+
|
|
941
|
+
new_source = _insert_path_resolver_import(new_source)
|
|
942
|
+
return new_source, len(replacements)
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
@register_action(
|
|
946
|
+
action_id="fix.path_resolver",
|
|
947
|
+
description="Rewrite hardcoded runtime directory literals to PathResolver accesses",
|
|
948
|
+
category=ActionCategory.FIX,
|
|
949
|
+
policies=["rules/architecture/path_access"],
|
|
950
|
+
remediates=["architecture.path_access.no_hardcoded_runtime_dirs"],
|
|
951
|
+
)
|
|
952
|
+
@atomic_action(
|
|
953
|
+
action_id="fix.path_resolver",
|
|
954
|
+
intent="Rewrite hardcoded runtime directory path construction to PathResolver",
|
|
955
|
+
impact=ActionImpact.WRITE_CODE,
|
|
956
|
+
policies=["atomic_actions"],
|
|
957
|
+
)
|
|
958
|
+
# ID: f5c8e2a4-9b7d-4a1e-b0f3-6c2d4e8a9b15
|
|
959
|
+
async def action_fix_path_resolver(
|
|
960
|
+
core_context: CoreContext, write: bool = False, **kwargs
|
|
961
|
+
) -> ActionResult:
|
|
962
|
+
"""Rewrite hardcoded runtime directory string literals to use PathResolver.
|
|
963
|
+
|
|
964
|
+
Two invocation modes (mirrors action_fix_placeholders):
|
|
965
|
+
|
|
966
|
+
1. Targeted (autonomous loop): caller supplies ``file_path`` in kwargs.
|
|
967
|
+
The action operates on exactly that file. Bounded scope per
|
|
968
|
+
invocation — matches the action's per-file impact contract.
|
|
969
|
+
|
|
970
|
+
2. Sweep (CLI / debugging): no ``file_path`` supplied. The action walks
|
|
971
|
+
every ``*.py`` under ``src/`` containing a runtime directory path literal
|
|
972
|
+
that the rule's regex flags, and rewrites each.
|
|
973
|
+
|
|
974
|
+
The rewrite is fully deterministic — no LLM call. ``_transform_path_resolver``
|
|
975
|
+
parses the file with ``ast``/``asttokens`` and replaces leaf
|
|
976
|
+
``BinOp(op=Div)`` nodes whose operand is ``Constant("reports")`` or
|
|
977
|
+
``Constant("logs")`` with ``PathResolver.from_repo(<other>).<dir>``.
|
|
978
|
+
The required ``from shared.path_resolver import PathResolver`` is
|
|
979
|
+
inserted into the file's imports if not already present.
|
|
980
|
+
|
|
981
|
+
Dry-run (``write=False``): returns a unified diff in ``data["diff"]``.
|
|
982
|
+
"""
|
|
983
|
+
import difflib
|
|
984
|
+
|
|
985
|
+
start = time.time()
|
|
986
|
+
repo_root: Path = core_context.git_service.repo_path
|
|
987
|
+
file_path = kwargs.get("file_path")
|
|
988
|
+
|
|
989
|
+
def _build_diff(original: str, rewritten: str, rel: str) -> str:
|
|
990
|
+
return "".join(
|
|
991
|
+
difflib.unified_diff(
|
|
992
|
+
original.splitlines(keepends=True),
|
|
993
|
+
rewritten.splitlines(keepends=True),
|
|
994
|
+
fromfile=f"a/{rel}",
|
|
995
|
+
tofile=f"b/{rel}",
|
|
996
|
+
n=3,
|
|
997
|
+
)
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
# ---- Targeted mode ------------------------------------------------------
|
|
1001
|
+
if file_path:
|
|
1002
|
+
try:
|
|
1003
|
+
target_rel = str(file_path).lstrip("./")
|
|
1004
|
+
target_abs = (repo_root / target_rel).resolve()
|
|
1005
|
+
|
|
1006
|
+
src_root = (repo_root / "src").resolve()
|
|
1007
|
+
try:
|
|
1008
|
+
target_abs.relative_to(src_root)
|
|
1009
|
+
except ValueError:
|
|
1010
|
+
return ActionResult(
|
|
1011
|
+
action_id="fix.path_resolver",
|
|
1012
|
+
ok=False,
|
|
1013
|
+
data={
|
|
1014
|
+
"error": f"Target outside src/ scope: {target_rel}",
|
|
1015
|
+
"file_path": target_rel,
|
|
1016
|
+
},
|
|
1017
|
+
duration_sec=time.time() - start,
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
if not target_abs.is_file() or target_abs.suffix != ".py":
|
|
1021
|
+
return ActionResult(
|
|
1022
|
+
action_id="fix.path_resolver",
|
|
1023
|
+
ok=False,
|
|
1024
|
+
data={
|
|
1025
|
+
"error": f"Target is not a .py file: {target_rel}",
|
|
1026
|
+
"file_path": target_rel,
|
|
1027
|
+
},
|
|
1028
|
+
duration_sec=time.time() - start,
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
original = target_abs.read_text(encoding="utf-8")
|
|
1032
|
+
|
|
1033
|
+
if not _RUNTIME_DIR_PATTERN.search(original):
|
|
1034
|
+
return ActionResult(
|
|
1035
|
+
action_id="fix.path_resolver",
|
|
1036
|
+
ok=True,
|
|
1037
|
+
data={
|
|
1038
|
+
"files_affected": 0,
|
|
1039
|
+
"written": False,
|
|
1040
|
+
"file_path": target_rel,
|
|
1041
|
+
"note": "no path_access literals matched",
|
|
1042
|
+
},
|
|
1043
|
+
duration_sec=time.time() - start,
|
|
1044
|
+
)
|
|
1045
|
+
|
|
1046
|
+
rewritten, n_replacements = _transform_path_resolver(original)
|
|
1047
|
+
|
|
1048
|
+
if n_replacements == 0 or rewritten == original:
|
|
1049
|
+
return ActionResult(
|
|
1050
|
+
action_id="fix.path_resolver",
|
|
1051
|
+
ok=True,
|
|
1052
|
+
data={
|
|
1053
|
+
"files_affected": 0,
|
|
1054
|
+
"written": False,
|
|
1055
|
+
"file_path": target_rel,
|
|
1056
|
+
"note": (
|
|
1057
|
+
"regex matched but no AST-level path-construction "
|
|
1058
|
+
"BinOp/Div sites — likely string literal in a "
|
|
1059
|
+
"non-path-construction context (e.g. exclude list, "
|
|
1060
|
+
"docstring example)"
|
|
1061
|
+
),
|
|
1062
|
+
},
|
|
1063
|
+
duration_sec=time.time() - start,
|
|
1064
|
+
)
|
|
1065
|
+
|
|
1066
|
+
if write:
|
|
1067
|
+
core_context.file_handler.write_runtime_text(target_rel, rewritten)
|
|
1068
|
+
|
|
1069
|
+
return ActionResult(
|
|
1070
|
+
action_id="fix.path_resolver",
|
|
1071
|
+
ok=True,
|
|
1072
|
+
data={
|
|
1073
|
+
"files_affected": 1,
|
|
1074
|
+
"replacements": n_replacements,
|
|
1075
|
+
"written": write,
|
|
1076
|
+
"file_path": target_rel,
|
|
1077
|
+
"diff": _build_diff(original, rewritten, target_rel),
|
|
1078
|
+
},
|
|
1079
|
+
duration_sec=time.time() - start,
|
|
1080
|
+
)
|
|
1081
|
+
except Exception as e:
|
|
1082
|
+
return ActionResult(
|
|
1083
|
+
action_id="fix.path_resolver",
|
|
1084
|
+
ok=False,
|
|
1085
|
+
data=_error_data(e, file_path=str(file_path)),
|
|
1086
|
+
duration_sec=time.time() - start,
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
# ---- Sweep mode ---------------------------------------------------------
|
|
1090
|
+
logger.warning(
|
|
1091
|
+
"fix.path_resolver invoked in sweep mode (no file_path). "
|
|
1092
|
+
"This mode is reserved for CLI callers; autonomous callers MUST "
|
|
1093
|
+
"supply file_path to stay within their declared impact scope."
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
files_modified = 0
|
|
1097
|
+
total_replacements = 0
|
|
1098
|
+
files_failed = 0
|
|
1099
|
+
files_skipped_no_match = 0
|
|
1100
|
+
|
|
1101
|
+
try:
|
|
1102
|
+
src_dir = repo_root / "src"
|
|
1103
|
+
for py_file in src_dir.rglob("*.py"):
|
|
1104
|
+
try:
|
|
1105
|
+
content = py_file.read_text(encoding="utf-8")
|
|
1106
|
+
if not _RUNTIME_DIR_PATTERN.search(content):
|
|
1107
|
+
files_skipped_no_match += 1
|
|
1108
|
+
continue
|
|
1109
|
+
|
|
1110
|
+
rewritten, n_replacements = _transform_path_resolver(content)
|
|
1111
|
+
if n_replacements == 0 or rewritten == content:
|
|
1112
|
+
continue
|
|
1113
|
+
|
|
1114
|
+
rel_path = str(py_file.relative_to(repo_root))
|
|
1115
|
+
if write:
|
|
1116
|
+
core_context.file_handler.write_runtime_text(rel_path, rewritten)
|
|
1117
|
+
files_modified += 1
|
|
1118
|
+
total_replacements += n_replacements
|
|
1119
|
+
except Exception as e:
|
|
1120
|
+
logger.warning("fix.path_resolver: failed on %s: %s", py_file, e)
|
|
1121
|
+
files_failed += 1
|
|
1122
|
+
|
|
1123
|
+
return ActionResult(
|
|
1124
|
+
action_id="fix.path_resolver",
|
|
1125
|
+
ok=files_failed == 0,
|
|
1126
|
+
data={
|
|
1127
|
+
"files_affected": files_modified,
|
|
1128
|
+
"replacements": total_replacements,
|
|
1129
|
+
"files_failed": files_failed,
|
|
1130
|
+
"files_skipped_no_match": files_skipped_no_match,
|
|
1131
|
+
"written": write,
|
|
1132
|
+
"mode": "sweep",
|
|
1133
|
+
},
|
|
1134
|
+
duration_sec=time.time() - start,
|
|
1135
|
+
)
|
|
1136
|
+
except Exception as e:
|
|
1137
|
+
return ActionResult(
|
|
1138
|
+
action_id="fix.path_resolver",
|
|
1139
|
+
ok=False,
|
|
1140
|
+
data=_error_data(e, mode="sweep"),
|
|
1141
|
+
duration_sec=time.time() - start,
|
|
1142
|
+
)
|