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,1168 @@
|
|
|
1
|
+
# src/mind/logic/engines/artifact_gate.py
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Constitutional Artifact Gate — PromptModel manifest validator.
|
|
5
|
+
|
|
6
|
+
Enforces structural and abstraction-boundary rules on var/prompts/*/model.yaml
|
|
7
|
+
files. Every check is deterministic: no LLM, no I/O beyond reading the file
|
|
8
|
+
already provided by the auditor.
|
|
9
|
+
|
|
10
|
+
Rules enforced:
|
|
11
|
+
- ai.prompt.artifact.required_fields — id, version, role, input.required,
|
|
12
|
+
output.format, success_criteria
|
|
13
|
+
- ai.prompt.artifact.no_provider_leak — model.preference must not name an
|
|
14
|
+
infrastructure provider
|
|
15
|
+
- ai.prompt.artifact.role_abstraction — role field must reference a cognitive
|
|
16
|
+
role, not a product or tool name
|
|
17
|
+
|
|
18
|
+
Vocabulary checks (ADR-023) operate on .specs/papers/CORE-Vocabulary.md and
|
|
19
|
+
.intent/META/vocabulary.json, not on YAML manifests. They are dispatched
|
|
20
|
+
before the YAML load so the engine never tries to parse Markdown as YAML.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import builtins
|
|
26
|
+
import importlib
|
|
27
|
+
import inspect
|
|
28
|
+
import re
|
|
29
|
+
import sys
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import TYPE_CHECKING, Any
|
|
32
|
+
|
|
33
|
+
import yaml
|
|
34
|
+
|
|
35
|
+
from shared.infrastructure.intent.cognitive_roles import load_cognitive_roles
|
|
36
|
+
from shared.infrastructure.intent.errors import GovernanceError
|
|
37
|
+
from shared.infrastructure.intent.filesystem_operations import (
|
|
38
|
+
FilesystemOperationTaxonomyError,
|
|
39
|
+
load_filesystem_operations,
|
|
40
|
+
)
|
|
41
|
+
from shared.infrastructure.intent.intent_repository import get_intent_repository
|
|
42
|
+
from shared.infrastructure.intent.vocabulary_projection import (
|
|
43
|
+
VOCABULARY_JSON_REL,
|
|
44
|
+
VOCABULARY_PAPER_REL,
|
|
45
|
+
VocabularyProjection,
|
|
46
|
+
VocabularyProjectionError,
|
|
47
|
+
compute_canonical_section_hash,
|
|
48
|
+
load_vocabulary_projection,
|
|
49
|
+
locate_canonical_section,
|
|
50
|
+
normalize_vocabulary_cell,
|
|
51
|
+
)
|
|
52
|
+
from shared.logger import getLogger
|
|
53
|
+
from shared.models import AuditFinding, AuditSeverity
|
|
54
|
+
|
|
55
|
+
from .base import BaseEngine, EngineResult
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if TYPE_CHECKING:
|
|
59
|
+
from mind.governance.audit_context import AuditorContext
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
logger = getLogger(__name__)
|
|
63
|
+
|
|
64
|
+
# Providers are infrastructure. They must not appear in model.yaml.
|
|
65
|
+
_KNOWN_PROVIDERS = {"anthropic", "openai", "deepseek", "ollama", "azure", "mistral"}
|
|
66
|
+
|
|
67
|
+
# Cognitive role names are loaded fail-closed from
|
|
68
|
+
# .intent/taxonomies/cognitive_roles.yaml (ADR-068 taxonomy pattern). See
|
|
69
|
+
# load_cognitive_roles for the contract; the gate never carries a literal
|
|
70
|
+
# role set, so role declarations land by editing the taxonomy, not this file.
|
|
71
|
+
|
|
72
|
+
_REQUIRED_TOP_LEVEL = {"id", "version", "role", "success_criteria"}
|
|
73
|
+
_REQUIRED_INPUT_SUBFIELDS = {"required"}
|
|
74
|
+
_REQUIRED_OUTPUT_SUBFIELDS = {"format"}
|
|
75
|
+
|
|
76
|
+
_VOCABULARY_CHECK_TYPES = frozenset(
|
|
77
|
+
{
|
|
78
|
+
"vocabulary_projection_consistency",
|
|
79
|
+
"vocabulary_canonical_format",
|
|
80
|
+
"vocabulary_authoritative_paths",
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
_GOVERNANCE_CHECK_TYPES = frozenset(
|
|
85
|
+
{
|
|
86
|
+
"all_rules_mapped",
|
|
87
|
+
"namespace_has_drainer",
|
|
88
|
+
"namespace_manifest_completeness",
|
|
89
|
+
"fs_operations_completeness",
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
_AUTO_REMEDIATION_REL = ".intent/enforcement/remediation/auto_remediation.yaml"
|
|
94
|
+
_RULES_DIR_REL = ".intent/rules"
|
|
95
|
+
_DRAINER_REGISTRY_REL = ".intent/enforcement/quarantine/drainer_registry.yaml"
|
|
96
|
+
_NAMESPACE_MANIFEST_REL = ".intent/governance/namespace_manifest.yaml"
|
|
97
|
+
_NAMESPACE_GOVERNED_ROOTS = (".intent", ".specs")
|
|
98
|
+
_MAPPING_KEY_RE = re.compile(r"^ ([a-z][a-z0-9_.]+):$", re.MULTILINE)
|
|
99
|
+
|
|
100
|
+
_REQUIRED_VOCAB_COLUMNS = ("term", "definition", "not", "authoritative_paper")
|
|
101
|
+
_GOVERNED_ROOTS = (".specs", ".intent")
|
|
102
|
+
_TABLE_SEPARATOR_RE = re.compile(r"^\|[\s\-|]+\|$")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _split_table_row(row: str) -> list[str]:
|
|
106
|
+
"""Parse one Markdown pipe-table row into stripped cells.
|
|
107
|
+
|
|
108
|
+
``"| foo | bar |"`` → ``["foo", "bar"]``. Leading/trailing whitespace and
|
|
109
|
+
the outer pipes are stripped before splitting on ``|``.
|
|
110
|
+
"""
|
|
111
|
+
stripped = row.strip().strip("|")
|
|
112
|
+
return [cell.strip() for cell in stripped.split("|")]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _parse_canonical_terms(
|
|
116
|
+
paper: Path,
|
|
117
|
+
) -> tuple[list[dict[str, str]], str | None]:
|
|
118
|
+
"""Parse the canonical-section table into a list of term dicts.
|
|
119
|
+
|
|
120
|
+
Returns ``(terms, error)``. On any parse failure returns ``([], error)``.
|
|
121
|
+
Each term dict has the four required column keys; rows with fewer than
|
|
122
|
+
four cells are skipped (the canonical_format check reports them).
|
|
123
|
+
"""
|
|
124
|
+
if not paper.is_file():
|
|
125
|
+
return [], f"Canonical paper missing: {paper}"
|
|
126
|
+
try:
|
|
127
|
+
text = paper.read_text(encoding="utf-8")
|
|
128
|
+
except OSError as exc:
|
|
129
|
+
return [], f"Cannot read canonical paper {paper}: {exc}"
|
|
130
|
+
|
|
131
|
+
rng = locate_canonical_section(text)
|
|
132
|
+
if rng is None:
|
|
133
|
+
return [], f"Canonical heading not found in {paper}"
|
|
134
|
+
start, end = rng
|
|
135
|
+
section_lines = text.splitlines()[start:end]
|
|
136
|
+
table_rows = [line for line in section_lines if line.lstrip().startswith("|")]
|
|
137
|
+
if len(table_rows) < 3:
|
|
138
|
+
return [], (
|
|
139
|
+
f"Canonical table needs header, separator, and at least one data "
|
|
140
|
+
f"row; found {len(table_rows)} pipe row(s)."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
sep_indices = [
|
|
144
|
+
i for i, row in enumerate(table_rows) if _TABLE_SEPARATOR_RE.match(row.strip())
|
|
145
|
+
]
|
|
146
|
+
if not sep_indices:
|
|
147
|
+
return [], "Canonical table has no separator row."
|
|
148
|
+
sep_idx = sep_indices[0]
|
|
149
|
+
if sep_idx == 0:
|
|
150
|
+
return [], "Separator row precedes header in canonical table."
|
|
151
|
+
|
|
152
|
+
terms: list[dict[str, str]] = []
|
|
153
|
+
for row in table_rows[sep_idx + 1 :]:
|
|
154
|
+
cells = _split_table_row(row)
|
|
155
|
+
if len(cells) < len(_REQUIRED_VOCAB_COLUMNS):
|
|
156
|
+
continue
|
|
157
|
+
terms.append(
|
|
158
|
+
{
|
|
159
|
+
"term": cells[0],
|
|
160
|
+
"definition": cells[1],
|
|
161
|
+
"not": cells[2],
|
|
162
|
+
"authoritative_paper": normalize_vocabulary_cell(cells[3]),
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
return terms, None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# -----------------------------------------------------------------------------
|
|
169
|
+
# Vocabulary check functions (module-level — pure, no instance state).
|
|
170
|
+
# Kept out of ArtifactGateEngine so the class stays under modularity limits.
|
|
171
|
+
# -----------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
_ENGINE_ID = "artifact_gate"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ID: 7e3b9c2f-4a18-4d65-b083-1f5a8c6e2d40
|
|
177
|
+
def _result_to_findings(
|
|
178
|
+
result: EngineResult, check_type: str, engine_id: str
|
|
179
|
+
) -> list[AuditFinding]:
|
|
180
|
+
"""Convert an EngineResult into AuditFindings for context-level dispatch.
|
|
181
|
+
|
|
182
|
+
ADR-076 D3. The repo-level check_* functions return EngineResult
|
|
183
|
+
(their per-file dispatch shape); ``verify_context`` must return
|
|
184
|
+
``list[AuditFinding]`` per the rule_executor contract for
|
|
185
|
+
context-level rules. Severity is a placeholder — rule_executor
|
|
186
|
+
overwrites it from the rule's declared enforcement.
|
|
187
|
+
"""
|
|
188
|
+
if result.ok:
|
|
189
|
+
return []
|
|
190
|
+
findings: list[AuditFinding] = []
|
|
191
|
+
for v in result.violations:
|
|
192
|
+
if isinstance(v, dict):
|
|
193
|
+
msg = str(v.get("message", ""))
|
|
194
|
+
details = dict(v.get("details") or {})
|
|
195
|
+
else:
|
|
196
|
+
msg = str(v)
|
|
197
|
+
details = {}
|
|
198
|
+
findings.append(
|
|
199
|
+
AuditFinding(
|
|
200
|
+
check_id=f"{engine_id}.{check_type}",
|
|
201
|
+
severity=AuditSeverity.INFO,
|
|
202
|
+
message=msg,
|
|
203
|
+
file_path="repo",
|
|
204
|
+
context=details,
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
return findings
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _vocab_result(check: str, violations: list[str]) -> EngineResult:
|
|
211
|
+
"""EngineResult builder for vocabulary checks.
|
|
212
|
+
|
|
213
|
+
Vocabulary checks span multiple files (paper + projection) so the
|
|
214
|
+
message references the check_type, not a single file_path.
|
|
215
|
+
"""
|
|
216
|
+
if not violations:
|
|
217
|
+
return EngineResult(
|
|
218
|
+
ok=True,
|
|
219
|
+
message=f"artifact_gate[{check}]: passed.",
|
|
220
|
+
violations=[],
|
|
221
|
+
engine_id=_ENGINE_ID,
|
|
222
|
+
)
|
|
223
|
+
return EngineResult(
|
|
224
|
+
ok=False,
|
|
225
|
+
message=f"artifact_gate[{check}]: {len(violations)} violation(s).",
|
|
226
|
+
violations=violations,
|
|
227
|
+
engine_id=_ENGINE_ID,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _validate_authoritative_path(
|
|
232
|
+
term: str, path: str, repo_root: Path, source: str
|
|
233
|
+
) -> list[str]:
|
|
234
|
+
"""Return violation strings for a single term's authoritative_paper value."""
|
|
235
|
+
if not path:
|
|
236
|
+
return [f"{source} term '{term}': authoritative_paper is empty."]
|
|
237
|
+
if not any(path.startswith(root + "/") for root in _GOVERNED_ROOTS):
|
|
238
|
+
return [
|
|
239
|
+
f"{source} term '{term}': authoritative_paper '{path}' must "
|
|
240
|
+
f"start with one of {list(_GOVERNED_ROOTS)} followed by '/'."
|
|
241
|
+
]
|
|
242
|
+
if not (repo_root / path).is_file():
|
|
243
|
+
return [
|
|
244
|
+
f"{source} term '{term}': authoritative_paper '{path}' does not "
|
|
245
|
+
"resolve to an existing file."
|
|
246
|
+
]
|
|
247
|
+
return []
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _check_projection_consistency(repo_root: Path, check: str) -> EngineResult:
|
|
251
|
+
"""Cross-check canonical Markdown section against the JSON projection."""
|
|
252
|
+
proj = load_vocabulary_projection(repo_root)
|
|
253
|
+
if isinstance(proj, VocabularyProjectionError):
|
|
254
|
+
return _vocab_result(check, [f"Projection broken: {proj.reason}"])
|
|
255
|
+
assert isinstance(proj, VocabularyProjection)
|
|
256
|
+
|
|
257
|
+
paper = repo_root / VOCABULARY_PAPER_REL
|
|
258
|
+
canonical_terms, parse_err = _parse_canonical_terms(paper)
|
|
259
|
+
if parse_err:
|
|
260
|
+
return _vocab_result(check, [parse_err])
|
|
261
|
+
|
|
262
|
+
canonical_names = {t["term"] for t in canonical_terms}
|
|
263
|
+
projection_names = {t.term for t in proj.terms}
|
|
264
|
+
|
|
265
|
+
violations: list[str] = []
|
|
266
|
+
for name in sorted(canonical_names - projection_names):
|
|
267
|
+
violations.append(
|
|
268
|
+
f"Canonical term '{name}' is absent from projection {VOCABULARY_JSON_REL}."
|
|
269
|
+
)
|
|
270
|
+
for name in sorted(projection_names - canonical_names):
|
|
271
|
+
violations.append(
|
|
272
|
+
f"Projection term '{name}' is absent from canonical section "
|
|
273
|
+
f"in {VOCABULARY_PAPER_REL}."
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
recomputed = compute_canonical_section_hash(repo_root)
|
|
277
|
+
if recomputed is not None and recomputed != proj.source_hash:
|
|
278
|
+
violations.append(
|
|
279
|
+
"source_hash mismatch: projection has "
|
|
280
|
+
f"'{proj.source_hash[:12]}', canonical section currently hashes "
|
|
281
|
+
f"to '{recomputed[:12]}'. Regenerate the projection via "
|
|
282
|
+
"sync_vocabulary."
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return _vocab_result(check, violations)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _check_canonical_format(repo_root: Path, check: str) -> EngineResult:
|
|
289
|
+
"""Validate the structural shape of the canonical-section Markdown table."""
|
|
290
|
+
from shared.infrastructure.intent.vocabulary_projection import CANONICAL_HEADING
|
|
291
|
+
|
|
292
|
+
paper = repo_root / VOCABULARY_PAPER_REL
|
|
293
|
+
if not paper.is_file():
|
|
294
|
+
return _vocab_result(
|
|
295
|
+
check, [f"Canonical paper missing: {VOCABULARY_PAPER_REL}"]
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
text = paper.read_text(encoding="utf-8")
|
|
299
|
+
lines = text.splitlines()
|
|
300
|
+
|
|
301
|
+
violations: list[str] = []
|
|
302
|
+
heading_count = sum(1 for line in lines if line.rstrip() == CANONICAL_HEADING)
|
|
303
|
+
if heading_count == 0:
|
|
304
|
+
violations.append(f"Canonical heading '{CANONICAL_HEADING}' not found.")
|
|
305
|
+
return _vocab_result(check, violations)
|
|
306
|
+
if heading_count > 1:
|
|
307
|
+
violations.append(
|
|
308
|
+
f"Canonical heading appears {heading_count} times; expected exactly 1."
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
rng = locate_canonical_section(text)
|
|
312
|
+
if rng is None:
|
|
313
|
+
violations.append("Canonical section could not be located.")
|
|
314
|
+
return _vocab_result(check, violations)
|
|
315
|
+
start, end = rng
|
|
316
|
+
section_lines = lines[start:end]
|
|
317
|
+
|
|
318
|
+
table_rows = [line for line in section_lines if line.lstrip().startswith("|")]
|
|
319
|
+
if not table_rows:
|
|
320
|
+
violations.append(
|
|
321
|
+
"No Markdown table found in canonical section (no rows starting with '|')."
|
|
322
|
+
)
|
|
323
|
+
return _vocab_result(check, violations)
|
|
324
|
+
|
|
325
|
+
sep_indices = [
|
|
326
|
+
i for i, row in enumerate(table_rows) if _TABLE_SEPARATOR_RE.match(row.strip())
|
|
327
|
+
]
|
|
328
|
+
if not sep_indices:
|
|
329
|
+
violations.append("No separator row matching r'^\\|[\\s\\-|]+\\|$' found.")
|
|
330
|
+
return _vocab_result(check, violations)
|
|
331
|
+
if len(sep_indices) > 1:
|
|
332
|
+
violations.append(
|
|
333
|
+
f"Found {len(sep_indices)} separator rows in canonical section; "
|
|
334
|
+
"expected exactly 1 (nested table not allowed)."
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
sep_idx = sep_indices[0]
|
|
338
|
+
if sep_idx == 0:
|
|
339
|
+
violations.append("Separator row precedes any header row.")
|
|
340
|
+
return _vocab_result(check, violations)
|
|
341
|
+
|
|
342
|
+
header_cells = _split_table_row(table_rows[sep_idx - 1])
|
|
343
|
+
first_four = [c.lower() for c in header_cells[: len(_REQUIRED_VOCAB_COLUMNS)]]
|
|
344
|
+
if first_four != list(_REQUIRED_VOCAB_COLUMNS):
|
|
345
|
+
violations.append(
|
|
346
|
+
"Header columns must start with "
|
|
347
|
+
f"{list(_REQUIRED_VOCAB_COLUMNS)} in that order; "
|
|
348
|
+
f"got {first_four}."
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
data_rows = table_rows[sep_idx + 1 :]
|
|
352
|
+
for i, row in enumerate(data_rows, start=1):
|
|
353
|
+
cells = _split_table_row(row)
|
|
354
|
+
if len(cells) < len(_REQUIRED_VOCAB_COLUMNS):
|
|
355
|
+
violations.append(
|
|
356
|
+
f"Row {i}: has {len(cells)} cell(s); requires at least "
|
|
357
|
+
f"{len(_REQUIRED_VOCAB_COLUMNS)}."
|
|
358
|
+
)
|
|
359
|
+
continue
|
|
360
|
+
for col_idx, col_name in enumerate(_REQUIRED_VOCAB_COLUMNS):
|
|
361
|
+
if not cells[col_idx]:
|
|
362
|
+
violations.append(f"Row {i}: column '{col_name}' is empty.")
|
|
363
|
+
for cell in cells:
|
|
364
|
+
if "<" in cell and ">" in cell:
|
|
365
|
+
violations.append(f"Row {i}: cell contains HTML markup: '{cell[:60]}'.")
|
|
366
|
+
break
|
|
367
|
+
if "![" in cell:
|
|
368
|
+
violations.append(
|
|
369
|
+
f"Row {i}: cell contains an inline image: '{cell[:60]}'."
|
|
370
|
+
)
|
|
371
|
+
break
|
|
372
|
+
|
|
373
|
+
return _vocab_result(check, violations)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _check_authoritative_paths(repo_root: Path, check: str) -> EngineResult:
|
|
377
|
+
"""Verify each term's authoritative_paper resolves under a governed root."""
|
|
378
|
+
paper = repo_root / VOCABULARY_PAPER_REL
|
|
379
|
+
canonical_terms, parse_err = _parse_canonical_terms(paper)
|
|
380
|
+
if parse_err:
|
|
381
|
+
return _vocab_result(check, [parse_err])
|
|
382
|
+
canonical_paths = {t["term"]: t["authoritative_paper"] for t in canonical_terms}
|
|
383
|
+
|
|
384
|
+
proj = load_vocabulary_projection(repo_root)
|
|
385
|
+
violations: list[str] = []
|
|
386
|
+
projection_paths: dict[str, str] = {}
|
|
387
|
+
if isinstance(proj, VocabularyProjectionError):
|
|
388
|
+
violations.append(f"Projection broken: {proj.reason}")
|
|
389
|
+
else:
|
|
390
|
+
assert isinstance(proj, VocabularyProjection)
|
|
391
|
+
projection_paths = {t.term: t.authoritative_paper for t in proj.terms}
|
|
392
|
+
|
|
393
|
+
for term, path in canonical_paths.items():
|
|
394
|
+
violations.extend(
|
|
395
|
+
_validate_authoritative_path(term, path, repo_root, "canonical")
|
|
396
|
+
)
|
|
397
|
+
for term, path in projection_paths.items():
|
|
398
|
+
violations.extend(
|
|
399
|
+
_validate_authoritative_path(term, path, repo_root, "projection")
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
for term in sorted(set(canonical_paths) & set(projection_paths)):
|
|
403
|
+
if canonical_paths[term] != projection_paths[term]:
|
|
404
|
+
violations.append(
|
|
405
|
+
f"Term '{term}': projection authoritative_paper "
|
|
406
|
+
f"'{projection_paths[term]}' != canonical "
|
|
407
|
+
f"'{canonical_paths[term]}'."
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
return _vocab_result(check, violations)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
# ID: 7d1a2c8e-4b3f-4d52-a6e1-9c2b8d4e1f7a
|
|
414
|
+
def _check_all_rules_mapped(repo_root: Path, check: str) -> EngineResult:
|
|
415
|
+
"""Verify every active reporting rule has an entry in auto_remediation.yaml.
|
|
416
|
+
|
|
417
|
+
ADR-066: rules with no remediation-map entry produce a silent
|
|
418
|
+
abandoned-finding re-emission loop. Scope is reporting rules only
|
|
419
|
+
— blocking rules fire pre-commit and do not enter the audit loop.
|
|
420
|
+
"""
|
|
421
|
+
map_file = repo_root / _AUTO_REMEDIATION_REL
|
|
422
|
+
rules_dir = repo_root / _RULES_DIR_REL
|
|
423
|
+
|
|
424
|
+
if not map_file.exists():
|
|
425
|
+
return EngineResult(
|
|
426
|
+
ok=False,
|
|
427
|
+
message=f"artifact_gate[{check}]: auto_remediation.yaml missing at {map_file}",
|
|
428
|
+
violations=[f"Configuration error: {_AUTO_REMEDIATION_REL} not found"],
|
|
429
|
+
engine_id=_ENGINE_ID,
|
|
430
|
+
)
|
|
431
|
+
if not rules_dir.is_dir():
|
|
432
|
+
return EngineResult(
|
|
433
|
+
ok=False,
|
|
434
|
+
message=f"artifact_gate[{check}]: rules dir missing at {rules_dir}",
|
|
435
|
+
violations=[f"Configuration error: {_RULES_DIR_REL} not found"],
|
|
436
|
+
engine_id=_ENGINE_ID,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
repo = get_intent_repository()
|
|
440
|
+
mapped_ids: set[str] = set(
|
|
441
|
+
_MAPPING_KEY_RE.findall(
|
|
442
|
+
repo.load_text(_AUTO_REMEDIATION_REL.removeprefix(".intent/"))
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
unmapped: list[str] = []
|
|
447
|
+
for ref in sorted(repo.list_policies(), key=lambda r: r.policy_id):
|
|
448
|
+
if not ref.policy_id.startswith("rules/"):
|
|
449
|
+
continue
|
|
450
|
+
if ref.path.suffix != ".json":
|
|
451
|
+
continue
|
|
452
|
+
try:
|
|
453
|
+
doc = repo.load_document(ref.path)
|
|
454
|
+
except GovernanceError:
|
|
455
|
+
# Defensive: a malformed rule document is a separate failure mode,
|
|
456
|
+
# not this rule's concern. Skip silently — other validators flag it.
|
|
457
|
+
continue
|
|
458
|
+
if not isinstance(doc, dict):
|
|
459
|
+
continue
|
|
460
|
+
if doc.get("metadata", {}).get("status") != "active":
|
|
461
|
+
continue
|
|
462
|
+
for r in doc.get("rules", []) or []:
|
|
463
|
+
if not isinstance(r, dict):
|
|
464
|
+
continue
|
|
465
|
+
rid = r.get("id")
|
|
466
|
+
if not isinstance(rid, str) or not rid:
|
|
467
|
+
continue
|
|
468
|
+
if r.get("enforcement") != "reporting":
|
|
469
|
+
continue
|
|
470
|
+
if rid in mapped_ids:
|
|
471
|
+
continue
|
|
472
|
+
unmapped.append(rid)
|
|
473
|
+
|
|
474
|
+
if not unmapped:
|
|
475
|
+
return _vocab_result(check, [])
|
|
476
|
+
|
|
477
|
+
violations = [
|
|
478
|
+
f"Active reporting rule '{rid}' has no entry in {_AUTO_REMEDIATION_REL}"
|
|
479
|
+
for rid in sorted(unmapped)
|
|
480
|
+
]
|
|
481
|
+
return _vocab_result(check, violations)
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
# ID: 5b8c2d1e-7a4f-4609-bc3a-d8e9f0a1b234
|
|
485
|
+
async def _check_namespace_has_drainer(
|
|
486
|
+
repo_root: Path,
|
|
487
|
+
check: str,
|
|
488
|
+
params: dict[str, Any],
|
|
489
|
+
) -> EngineResult:
|
|
490
|
+
"""Verify every awaiting_reaudit subject namespace has a registered drainer.
|
|
491
|
+
|
|
492
|
+
ADR-072. The check enumerates DISTINCT subject prefixes currently
|
|
493
|
+
held in ``awaiting_reaudit`` and compares them to the prefixes
|
|
494
|
+
registered in ``drainer_registry.yaml``. A prefix present in the
|
|
495
|
+
DB but absent from the registry is an unmapped quarantine namespace.
|
|
496
|
+
|
|
497
|
+
DB access uses the audit-driver-injected session via
|
|
498
|
+
``params['_context'].db_session`` (mirrors llm_gate's pattern —
|
|
499
|
+
Mind layer cannot open sessions itself). If no session is injected
|
|
500
|
+
(e.g. IntentGuard pre-commit path), the check returns ok=True with
|
|
501
|
+
a deferred-to-audit-time message rather than failing — the check
|
|
502
|
+
is meaningful only when the live blackboard state is accessible.
|
|
503
|
+
"""
|
|
504
|
+
from sqlalchemy import text
|
|
505
|
+
|
|
506
|
+
registry_file = repo_root / _DRAINER_REGISTRY_REL
|
|
507
|
+
if not registry_file.exists():
|
|
508
|
+
return EngineResult(
|
|
509
|
+
ok=False,
|
|
510
|
+
message=(
|
|
511
|
+
f"artifact_gate[{check}]: drainer_registry.yaml missing at "
|
|
512
|
+
f"{registry_file}"
|
|
513
|
+
),
|
|
514
|
+
violations=[f"Configuration error: {_DRAINER_REGISTRY_REL} not found"],
|
|
515
|
+
engine_id=_ENGINE_ID,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
repo = get_intent_repository()
|
|
519
|
+
try:
|
|
520
|
+
registry_doc = repo.load_document(
|
|
521
|
+
repo.resolve_rel(_DRAINER_REGISTRY_REL.removeprefix(".intent/"))
|
|
522
|
+
)
|
|
523
|
+
except GovernanceError as exc:
|
|
524
|
+
return EngineResult(
|
|
525
|
+
ok=False,
|
|
526
|
+
message=f"artifact_gate[{check}]: cannot load drainer_registry.yaml: {exc}",
|
|
527
|
+
violations=[f"Configuration error: {_DRAINER_REGISTRY_REL} unloadable"],
|
|
528
|
+
engine_id=_ENGINE_ID,
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
if not isinstance(registry_doc, dict):
|
|
532
|
+
return EngineResult(
|
|
533
|
+
ok=False,
|
|
534
|
+
message=f"artifact_gate[{check}]: drainer_registry.yaml not a mapping",
|
|
535
|
+
violations=[
|
|
536
|
+
f"Configuration error: {_DRAINER_REGISTRY_REL} top-level "
|
|
537
|
+
"must be a YAML mapping"
|
|
538
|
+
],
|
|
539
|
+
engine_id=_ENGINE_ID,
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
raw_entries = registry_doc.get("namespaces") or []
|
|
543
|
+
registered_normalized: set[str] = set()
|
|
544
|
+
for entry in raw_entries:
|
|
545
|
+
if not isinstance(entry, dict):
|
|
546
|
+
continue
|
|
547
|
+
prefix = entry.get("prefix")
|
|
548
|
+
if isinstance(prefix, str) and prefix:
|
|
549
|
+
# Normalise by stripping trailing colons so both "audit.violation::"
|
|
550
|
+
# and "audit.violation" registry forms match the bare namespace
|
|
551
|
+
# returned by SPLIT_PART.
|
|
552
|
+
registered_normalized.add(prefix.rstrip(":"))
|
|
553
|
+
|
|
554
|
+
# DB session is injected by the audit driver; absence is a
|
|
555
|
+
# degraded-mode signal, not a check failure (see docstring).
|
|
556
|
+
auditor_context = params.get("_context")
|
|
557
|
+
session = getattr(auditor_context, "db_session", None)
|
|
558
|
+
if session is None:
|
|
559
|
+
return EngineResult(
|
|
560
|
+
ok=True,
|
|
561
|
+
message=(
|
|
562
|
+
f"artifact_gate[{check}]: DB session not injected — check "
|
|
563
|
+
"deferred to audit-time run that injects auditor_context.db_session."
|
|
564
|
+
),
|
|
565
|
+
violations=[],
|
|
566
|
+
engine_id=_ENGINE_ID,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
result = await session.execute(
|
|
570
|
+
text(
|
|
571
|
+
"""
|
|
572
|
+
SELECT DISTINCT SPLIT_PART(subject, '::', 1) AS namespace
|
|
573
|
+
FROM core.blackboard_entries
|
|
574
|
+
WHERE entry_type = 'finding'
|
|
575
|
+
AND status = 'awaiting_reaudit'
|
|
576
|
+
AND resolved_at IS NULL
|
|
577
|
+
"""
|
|
578
|
+
)
|
|
579
|
+
)
|
|
580
|
+
quarantine_namespaces = {row[0] for row in result.fetchall() if row[0]}
|
|
581
|
+
|
|
582
|
+
unmapped = sorted(quarantine_namespaces - registered_normalized)
|
|
583
|
+
if not unmapped:
|
|
584
|
+
return EngineResult(
|
|
585
|
+
ok=True,
|
|
586
|
+
message=f"artifact_gate[{check}]: all quarantine namespaces have registered drainers.",
|
|
587
|
+
violations=[],
|
|
588
|
+
engine_id=_ENGINE_ID,
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
violations = [
|
|
592
|
+
f"Subject namespace '{ns}' has rows in awaiting_reaudit but no "
|
|
593
|
+
f"drainer is registered in {_DRAINER_REGISTRY_REL}"
|
|
594
|
+
for ns in unmapped
|
|
595
|
+
]
|
|
596
|
+
return EngineResult(
|
|
597
|
+
ok=False,
|
|
598
|
+
message=(
|
|
599
|
+
f"artifact_gate[{check}]: {len(unmapped)} unmapped quarantine "
|
|
600
|
+
"namespace(s) — ADR-072 invariant violated."
|
|
601
|
+
),
|
|
602
|
+
violations=violations,
|
|
603
|
+
engine_id=_ENGINE_ID,
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
# ID: 742276f9-7865-4073-a345-1256089d91c8
|
|
608
|
+
def _check_namespace_manifest_completeness(repo_root: Path, check: str) -> EngineResult:
|
|
609
|
+
"""Verify every file under .intent/ and .specs/ has a manifest entry.
|
|
610
|
+
|
|
611
|
+
ADR-075 D7. The check is a structural set-difference between the
|
|
612
|
+
filesystem walk under the governed roots (.intent/ + .specs/) and the
|
|
613
|
+
paths declared in .intent/governance/namespace_manifest.yaml's
|
|
614
|
+
``classifications`` list. A file present on disk with no manifest
|
|
615
|
+
entry is unclassified and surfaces as a violation under this rule.
|
|
616
|
+
"""
|
|
617
|
+
manifest_file = repo_root / _NAMESPACE_MANIFEST_REL
|
|
618
|
+
if not manifest_file.exists():
|
|
619
|
+
return EngineResult(
|
|
620
|
+
ok=False,
|
|
621
|
+
message=(
|
|
622
|
+
f"artifact_gate[{check}]: namespace_manifest.yaml missing at "
|
|
623
|
+
f"{manifest_file}"
|
|
624
|
+
),
|
|
625
|
+
violations=[f"Configuration error: {_NAMESPACE_MANIFEST_REL} not found"],
|
|
626
|
+
engine_id=_ENGINE_ID,
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
try:
|
|
630
|
+
manifest_doc = yaml.safe_load(manifest_file.read_text(encoding="utf-8")) or {}
|
|
631
|
+
except yaml.YAMLError as exc:
|
|
632
|
+
return EngineResult(
|
|
633
|
+
ok=False,
|
|
634
|
+
message=f"artifact_gate[{check}]: cannot parse namespace_manifest.yaml: {exc}",
|
|
635
|
+
violations=[f"Configuration error: {_NAMESPACE_MANIFEST_REL} unparseable"],
|
|
636
|
+
engine_id=_ENGINE_ID,
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
if not isinstance(manifest_doc, dict):
|
|
640
|
+
return EngineResult(
|
|
641
|
+
ok=False,
|
|
642
|
+
message=f"artifact_gate[{check}]: namespace_manifest.yaml not a mapping",
|
|
643
|
+
violations=[
|
|
644
|
+
f"Configuration error: {_NAMESPACE_MANIFEST_REL} top-level "
|
|
645
|
+
"must be a YAML mapping"
|
|
646
|
+
],
|
|
647
|
+
engine_id=_ENGINE_ID,
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
classified: set[str] = set()
|
|
651
|
+
for entry in manifest_doc.get("classifications") or []:
|
|
652
|
+
if not isinstance(entry, dict):
|
|
653
|
+
continue
|
|
654
|
+
path = entry.get("path")
|
|
655
|
+
if isinstance(path, str) and path:
|
|
656
|
+
classified.add(path)
|
|
657
|
+
|
|
658
|
+
fs_paths: set[str] = set()
|
|
659
|
+
for root_name in _NAMESPACE_GOVERNED_ROOTS:
|
|
660
|
+
root_dir = repo_root / root_name
|
|
661
|
+
if not root_dir.is_dir():
|
|
662
|
+
continue
|
|
663
|
+
for p in root_dir.rglob("*"):
|
|
664
|
+
if p.is_file():
|
|
665
|
+
fs_paths.add(str(p.relative_to(repo_root)))
|
|
666
|
+
|
|
667
|
+
unclassified = sorted(fs_paths - classified)
|
|
668
|
+
if not unclassified:
|
|
669
|
+
return _vocab_result(check, [])
|
|
670
|
+
|
|
671
|
+
violations = [
|
|
672
|
+
f"File '{path}' has no entry in {_NAMESPACE_MANIFEST_REL}"
|
|
673
|
+
for path in unclassified
|
|
674
|
+
]
|
|
675
|
+
return _vocab_result(check, violations)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
# ID: f68dc080-017d-42ee-8580-095b44bf668d
|
|
679
|
+
def _check_fs_operations_completeness(repo_root: Path, check: str) -> EngineResult:
|
|
680
|
+
"""Verify the filesystem-operation taxonomy covers the runtime FS surface.
|
|
681
|
+
|
|
682
|
+
ADR-077 §3. Three-part introspective set-difference against the loaded
|
|
683
|
+
taxonomy:
|
|
684
|
+
|
|
685
|
+
- Every public method on pathlib.Path must appear in operations.pathlib_path
|
|
686
|
+
(auto-discovery; absence is a finding). Stale declarations whose names no
|
|
687
|
+
longer resolve to runtime Path methods are also surfaced.
|
|
688
|
+
- Every entry in operations.watched must still resolve to a callable in its
|
|
689
|
+
module (catches stdlib rename/removal or third-party absence). Mixed
|
|
690
|
+
namespaces (os/shutil/tempfile/builtin open) are not auto-discovered —
|
|
691
|
+
§3 explicitly excludes that.
|
|
692
|
+
- The taxonomy's declared python_version must match runtime major.minor
|
|
693
|
+
(§5 determinism anchor).
|
|
694
|
+
|
|
695
|
+
Remediation is manual_review per §3: classifying a newly surfaced call is a
|
|
696
|
+
governance decision (read vs write), not auto-remediation territory.
|
|
697
|
+
"""
|
|
698
|
+
try:
|
|
699
|
+
taxonomy = load_filesystem_operations(repo_root)
|
|
700
|
+
except FilesystemOperationTaxonomyError as exc:
|
|
701
|
+
return EngineResult(
|
|
702
|
+
ok=False,
|
|
703
|
+
message=(
|
|
704
|
+
f"artifact_gate[{check}]: filesystem-operation taxonomy failed to load."
|
|
705
|
+
),
|
|
706
|
+
violations=[f"Taxonomy load error: {exc}"],
|
|
707
|
+
engine_id=_ENGINE_ID,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
violations: list[str] = []
|
|
711
|
+
|
|
712
|
+
declared_pathlib = {entry.name for entry in taxonomy.pathlib_path}
|
|
713
|
+
discovered_pathlib = {
|
|
714
|
+
name
|
|
715
|
+
for name, _ in inspect.getmembers(Path, predicate=callable)
|
|
716
|
+
if not name.startswith("_")
|
|
717
|
+
}
|
|
718
|
+
for name in sorted(discovered_pathlib - declared_pathlib):
|
|
719
|
+
violations.append(
|
|
720
|
+
f"pathlib.Path public method '{name}' is present in the runtime "
|
|
721
|
+
f"but absent from filesystem_operations.yaml "
|
|
722
|
+
f"operations.pathlib_path — classify it (manual_review)."
|
|
723
|
+
)
|
|
724
|
+
for name in sorted(declared_pathlib - discovered_pathlib):
|
|
725
|
+
violations.append(
|
|
726
|
+
f"filesystem_operations.yaml operations.pathlib_path declares "
|
|
727
|
+
f"'{name}' which is not a public method on pathlib.Path in this "
|
|
728
|
+
f"runtime — remove the stale entry or re-verify against the "
|
|
729
|
+
f"declared python_version (ADR-077 §5)."
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
for entry in sorted(taxonomy.watched, key=lambda e: e.name):
|
|
733
|
+
ok, reason = _resolve_watched_call(entry.name)
|
|
734
|
+
if not ok:
|
|
735
|
+
violations.append(
|
|
736
|
+
f"watched filesystem operation '{entry.name}' does not resolve "
|
|
737
|
+
f"to a callable: {reason} — update the taxonomy or restore the "
|
|
738
|
+
f"sanctioned import (manual_review)."
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
runtime_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
742
|
+
if taxonomy.python_version != runtime_version:
|
|
743
|
+
violations.append(
|
|
744
|
+
f"filesystem_operations.yaml declares python_version "
|
|
745
|
+
f"'{taxonomy.python_version}' but runtime is '{runtime_version}'; "
|
|
746
|
+
f"re-verify classifications against {runtime_version} and update "
|
|
747
|
+
f"the taxonomy header (ADR-077 §5)."
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
return _vocab_result(check, violations)
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
# ID: 269e97f0-0707-479c-b005-a3c23cd8a017
|
|
754
|
+
def _resolve_watched_call(qualified: str) -> tuple[bool, str]:
|
|
755
|
+
"""Resolve a dotted module.attr name (or bare builtin) to a callable.
|
|
756
|
+
|
|
757
|
+
Returns ``(True, "")`` on success; ``(False, reason)`` on any failure.
|
|
758
|
+
Bare names (no dot) resolve against ``builtins`` — e.g. ``"open"`` →
|
|
759
|
+
``builtins.open``. Dotted names import the leftmost segment and walk
|
|
760
|
+
``getattr`` through the remainder.
|
|
761
|
+
"""
|
|
762
|
+
if "." not in qualified:
|
|
763
|
+
target = getattr(builtins, qualified, None)
|
|
764
|
+
if target is None:
|
|
765
|
+
return False, f"builtins.{qualified} not found"
|
|
766
|
+
if not callable(target):
|
|
767
|
+
return False, f"builtins.{qualified} is not callable"
|
|
768
|
+
return True, ""
|
|
769
|
+
|
|
770
|
+
head, _, rest = qualified.partition(".")
|
|
771
|
+
try:
|
|
772
|
+
module = importlib.import_module(head)
|
|
773
|
+
except ImportError as exc:
|
|
774
|
+
return False, f"cannot import module '{head}': {exc}"
|
|
775
|
+
|
|
776
|
+
obj: Any = module
|
|
777
|
+
path_so_far = head
|
|
778
|
+
for part in rest.split("."):
|
|
779
|
+
obj = getattr(obj, part, None)
|
|
780
|
+
path_so_far = f"{path_so_far}.{part}"
|
|
781
|
+
if obj is None:
|
|
782
|
+
return False, f"{path_so_far} not found"
|
|
783
|
+
if not callable(obj):
|
|
784
|
+
return False, f"{qualified} is not callable"
|
|
785
|
+
return True, ""
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
# ID: 69841a82-0920-480c-94cb-d5e4b6cb50dd
|
|
789
|
+
class ArtifactGateEngine(BaseEngine):
|
|
790
|
+
"""
|
|
791
|
+
Constitutional validator for PromptModel artifact manifests.
|
|
792
|
+
|
|
793
|
+
Checks model.yaml files for structural completeness and abstraction
|
|
794
|
+
boundary compliance. All checks are purely mechanical — no LLM involved.
|
|
795
|
+
"""
|
|
796
|
+
|
|
797
|
+
engine_id = "artifact_gate"
|
|
798
|
+
|
|
799
|
+
@classmethod
|
|
800
|
+
# ID: 3c7b9d12-58a4-4e6f-8c1d-2a9e6b4f3d70
|
|
801
|
+
def is_context_level_for(cls, check_type: str | None) -> bool:
|
|
802
|
+
"""
|
|
803
|
+
ADR-076 D1/D2/D3: artifact_gate is mixed-mode.
|
|
804
|
+
|
|
805
|
+
Seven repo-level check_types (vocabulary projection/canonical_format/
|
|
806
|
+
authoritative_paths + governance all_rules_mapped/namespace_has_drainer/
|
|
807
|
+
namespace_manifest_completeness/fs_operations_completeness) dispatch
|
|
808
|
+
context-level. The three PromptModel check_types (required_fields,
|
|
809
|
+
no_provider_leak, role_abstraction) dispatch per-file over
|
|
810
|
+
var/prompts/**/model.yaml.
|
|
811
|
+
"""
|
|
812
|
+
if check_type is None:
|
|
813
|
+
return False
|
|
814
|
+
return (
|
|
815
|
+
check_type in _VOCABULARY_CHECK_TYPES
|
|
816
|
+
or check_type in _GOVERNANCE_CHECK_TYPES
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
# ID: 46226873-bc5a-4258-b3c8-476b0cfc878a
|
|
820
|
+
async def verify(self, file_path: Path, params: dict[str, Any]) -> EngineResult:
|
|
821
|
+
"""
|
|
822
|
+
Validate a model.yaml file against constitutional artifact rules.
|
|
823
|
+
|
|
824
|
+
Dispatches to a specific check based on params['check_type'].
|
|
825
|
+
Supported check types:
|
|
826
|
+
- required_fields — structural completeness
|
|
827
|
+
- no_provider_leak — preference field abstraction boundary
|
|
828
|
+
- role_abstraction — role field must be a cognitive role
|
|
829
|
+
|
|
830
|
+
Args:
|
|
831
|
+
file_path: Absolute path to the model.yaml being audited.
|
|
832
|
+
params: Rule parameters from enforcement mapping.
|
|
833
|
+
|
|
834
|
+
Returns:
|
|
835
|
+
EngineResult with violations list; ok=True means compliant.
|
|
836
|
+
"""
|
|
837
|
+
check_type = params.get("check_type", "required_fields")
|
|
838
|
+
|
|
839
|
+
if not file_path.exists():
|
|
840
|
+
return EngineResult(
|
|
841
|
+
ok=False,
|
|
842
|
+
message=f"Artifact file not found: {file_path}",
|
|
843
|
+
violations=[f"Missing file: {file_path}"],
|
|
844
|
+
engine_id=self.engine_id,
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
# Dispatch vocabulary checks BEFORE the YAML load. They operate on
|
|
848
|
+
# Markdown + JSON, not on PromptModel manifests, and pass through
|
|
849
|
+
# repo-level walks rather than reading file_path as YAML.
|
|
850
|
+
if check_type in _VOCABULARY_CHECK_TYPES:
|
|
851
|
+
return self._check_vocabulary(file_path, check_type)
|
|
852
|
+
|
|
853
|
+
# Governance meta-checks (ADR-066, ADR-072): repo-level invariants
|
|
854
|
+
# over .intent/ content and blackboard state. Dispatched before YAML
|
|
855
|
+
# load for the same reason.
|
|
856
|
+
if check_type in _GOVERNANCE_CHECK_TYPES:
|
|
857
|
+
return await self._check_governance(file_path, check_type, params)
|
|
858
|
+
|
|
859
|
+
try:
|
|
860
|
+
raw = yaml.safe_load(file_path.read_text(encoding="utf-8")) or {}
|
|
861
|
+
except Exception as exc:
|
|
862
|
+
return EngineResult(
|
|
863
|
+
ok=False,
|
|
864
|
+
message=f"Cannot parse model.yaml: {exc}",
|
|
865
|
+
violations=[f"YAML parse error in {file_path}: {exc}"],
|
|
866
|
+
engine_id=self.engine_id,
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
if check_type == "required_fields":
|
|
870
|
+
return self._check_required_fields(file_path, raw)
|
|
871
|
+
if check_type == "no_provider_leak":
|
|
872
|
+
return self._check_no_provider_leak(file_path, raw)
|
|
873
|
+
if check_type == "role_abstraction":
|
|
874
|
+
return self._check_role_abstraction(file_path, raw)
|
|
875
|
+
|
|
876
|
+
return EngineResult(
|
|
877
|
+
ok=False,
|
|
878
|
+
message=f"Unknown artifact_gate check_type: {check_type}",
|
|
879
|
+
violations=[f"Configuration error: unknown check_type '{check_type}'"],
|
|
880
|
+
engine_id=self.engine_id,
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
# -------------------------------------------------------------------------
|
|
884
|
+
# Checks
|
|
885
|
+
# -------------------------------------------------------------------------
|
|
886
|
+
|
|
887
|
+
# ID: 48ae4ead-2944-4c5a-8c8f-1423aad53497
|
|
888
|
+
def _check_required_fields(
|
|
889
|
+
self, file_path: Path, manifest: dict[str, Any]
|
|
890
|
+
) -> EngineResult:
|
|
891
|
+
"""
|
|
892
|
+
Verify all mandatory top-level and nested fields are present and
|
|
893
|
+
non-empty in a model.yaml manifest.
|
|
894
|
+
"""
|
|
895
|
+
violations: list[str] = []
|
|
896
|
+
|
|
897
|
+
for field in _REQUIRED_TOP_LEVEL:
|
|
898
|
+
if not manifest.get(field):
|
|
899
|
+
violations.append(f"Missing or empty required field: '{field}'")
|
|
900
|
+
|
|
901
|
+
input_block = manifest.get("input", {})
|
|
902
|
+
if not isinstance(input_block, dict):
|
|
903
|
+
violations.append("'input' must be a mapping")
|
|
904
|
+
else:
|
|
905
|
+
for sub in _REQUIRED_INPUT_SUBFIELDS:
|
|
906
|
+
if sub not in input_block:
|
|
907
|
+
violations.append(f"Missing required field: 'input.{sub}'")
|
|
908
|
+
|
|
909
|
+
output_block = manifest.get("output", {})
|
|
910
|
+
if not isinstance(output_block, dict):
|
|
911
|
+
violations.append("'output' must be a mapping")
|
|
912
|
+
else:
|
|
913
|
+
for sub in _REQUIRED_OUTPUT_SUBFIELDS:
|
|
914
|
+
if sub not in output_block:
|
|
915
|
+
violations.append(f"Missing required field: 'output.{sub}'")
|
|
916
|
+
|
|
917
|
+
return self._result(file_path, violations, "required_fields")
|
|
918
|
+
|
|
919
|
+
# ID: 33aaf189-b193-4fd9-bf4f-951098928e29
|
|
920
|
+
def _check_no_provider_leak(
|
|
921
|
+
self, file_path: Path, manifest: dict[str, Any]
|
|
922
|
+
) -> EngineResult:
|
|
923
|
+
"""
|
|
924
|
+
Ensure model.yaml does not reference infrastructure providers in the
|
|
925
|
+
model.preference field. Preference must name a capability class
|
|
926
|
+
(e.g. 'local', 'fast') or be absent — never a product name.
|
|
927
|
+
"""
|
|
928
|
+
violations: list[str] = []
|
|
929
|
+
|
|
930
|
+
model_block = manifest.get("model", {})
|
|
931
|
+
if isinstance(model_block, dict):
|
|
932
|
+
preference = str(model_block.get("preference", "")).lower().strip()
|
|
933
|
+
if preference in _KNOWN_PROVIDERS:
|
|
934
|
+
violations.append(
|
|
935
|
+
f"model.preference '{preference}' names an infrastructure provider. "
|
|
936
|
+
"Use a capability class (e.g. 'local', 'fast') or remove the field. "
|
|
937
|
+
"Provider routing belongs in CognitiveService role configuration, "
|
|
938
|
+
"not in a prompt artifact."
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
return self._result(file_path, violations, "no_provider_leak")
|
|
942
|
+
|
|
943
|
+
# ID: b984e44a-819b-4bc7-a0d9-e4272757002c
|
|
944
|
+
def _check_role_abstraction(
|
|
945
|
+
self, file_path: Path, manifest: dict[str, Any]
|
|
946
|
+
) -> EngineResult:
|
|
947
|
+
"""
|
|
948
|
+
Verify the role field references a declared cognitive role, not a
|
|
949
|
+
product, tool, or arbitrary string.
|
|
950
|
+
"""
|
|
951
|
+
violations: list[str] = []
|
|
952
|
+
|
|
953
|
+
role = str(manifest.get("role", "")).strip()
|
|
954
|
+
if not role:
|
|
955
|
+
violations.append("'role' field is missing or empty.")
|
|
956
|
+
else:
|
|
957
|
+
known_roles = load_cognitive_roles()
|
|
958
|
+
if role not in known_roles:
|
|
959
|
+
violations.append(
|
|
960
|
+
f"role '{role}' is not a declared cognitive role. "
|
|
961
|
+
f"Known roles: {sorted(known_roles)}. "
|
|
962
|
+
"If this is a new role, declare it in .intent/ first."
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
return self._result(file_path, violations, "role_abstraction")
|
|
966
|
+
|
|
967
|
+
# -------------------------------------------------------------------------
|
|
968
|
+
# Helpers
|
|
969
|
+
# -------------------------------------------------------------------------
|
|
970
|
+
|
|
971
|
+
def _result(
|
|
972
|
+
self, file_path: Path, violations: list[str], check: str
|
|
973
|
+
) -> EngineResult:
|
|
974
|
+
"""Build a standardised EngineResult for an artifact_gate check."""
|
|
975
|
+
if not violations:
|
|
976
|
+
return EngineResult(
|
|
977
|
+
ok=True,
|
|
978
|
+
message=f"artifact_gate[{check}]: {file_path.name} passed.",
|
|
979
|
+
violations=[],
|
|
980
|
+
engine_id=self.engine_id,
|
|
981
|
+
)
|
|
982
|
+
return EngineResult(
|
|
983
|
+
ok=False,
|
|
984
|
+
message=f"artifact_gate[{check}]: {len(violations)} violation(s) in {file_path.name}.",
|
|
985
|
+
violations=violations,
|
|
986
|
+
engine_id=self.engine_id,
|
|
987
|
+
)
|
|
988
|
+
|
|
989
|
+
# -------------------------------------------------------------------------
|
|
990
|
+
# Vocabulary checks (ADR-023)
|
|
991
|
+
# Dispatch only — the actual check functions live at module level so the
|
|
992
|
+
# class stays under modularity.class_too_large limits.
|
|
993
|
+
# -------------------------------------------------------------------------
|
|
994
|
+
|
|
995
|
+
def _check_vocabulary(self, file_path: Path, check_type: str) -> EngineResult:
|
|
996
|
+
"""Locate the repo root from file_path and dispatch the vocabulary check."""
|
|
997
|
+
repo_root: Path | None = None
|
|
998
|
+
for parent in file_path.resolve().parents:
|
|
999
|
+
if (parent / ".intent").is_dir() and (parent / ".specs").is_dir():
|
|
1000
|
+
repo_root = parent
|
|
1001
|
+
break
|
|
1002
|
+
if repo_root is None:
|
|
1003
|
+
return EngineResult(
|
|
1004
|
+
ok=False,
|
|
1005
|
+
message=f"artifact_gate[{check_type}]: cannot locate repo root.",
|
|
1006
|
+
violations=[
|
|
1007
|
+
f"Configuration error: walked up from {file_path} but found "
|
|
1008
|
+
"no directory containing both .intent/ and .specs/."
|
|
1009
|
+
],
|
|
1010
|
+
engine_id=self.engine_id,
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
if check_type == "vocabulary_projection_consistency":
|
|
1014
|
+
return _check_projection_consistency(repo_root, check_type)
|
|
1015
|
+
if check_type == "vocabulary_canonical_format":
|
|
1016
|
+
return _check_canonical_format(repo_root, check_type)
|
|
1017
|
+
if check_type == "vocabulary_authoritative_paths":
|
|
1018
|
+
return _check_authoritative_paths(repo_root, check_type)
|
|
1019
|
+
|
|
1020
|
+
# Should never reach here — verify() filters check_type — but keep
|
|
1021
|
+
# a defensive return in case the dispatch table drifts.
|
|
1022
|
+
return EngineResult(
|
|
1023
|
+
ok=False,
|
|
1024
|
+
message=f"artifact_gate[{check_type}]: dispatch fall-through.",
|
|
1025
|
+
violations=[f"Unknown vocabulary check_type '{check_type}'"],
|
|
1026
|
+
engine_id=self.engine_id,
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
# -------------------------------------------------------------------------
|
|
1030
|
+
# Governance meta-checks (ADR-066)
|
|
1031
|
+
# Dispatch only — the actual check functions live at module level so the
|
|
1032
|
+
# class stays under modularity.class_too_large limits.
|
|
1033
|
+
# -------------------------------------------------------------------------
|
|
1034
|
+
|
|
1035
|
+
async def _check_governance(
|
|
1036
|
+
self, file_path: Path, check_type: str, params: dict[str, Any]
|
|
1037
|
+
) -> EngineResult:
|
|
1038
|
+
"""Locate the repo root from file_path and dispatch the governance check.
|
|
1039
|
+
|
|
1040
|
+
Dispatcher is async because ``namespace_has_drainer`` (ADR-072 D4)
|
|
1041
|
+
runs a DB query via the injected auditor_context.db_session.
|
|
1042
|
+
``all_rules_mapped`` remains synchronous internally.
|
|
1043
|
+
"""
|
|
1044
|
+
repo_root: Path | None = None
|
|
1045
|
+
for parent in file_path.resolve().parents:
|
|
1046
|
+
if (parent / ".intent").is_dir() and (parent / ".specs").is_dir():
|
|
1047
|
+
repo_root = parent
|
|
1048
|
+
break
|
|
1049
|
+
if repo_root is None:
|
|
1050
|
+
return EngineResult(
|
|
1051
|
+
ok=False,
|
|
1052
|
+
message=f"artifact_gate[{check_type}]: cannot locate repo root.",
|
|
1053
|
+
violations=[
|
|
1054
|
+
f"Configuration error: walked up from {file_path} but found "
|
|
1055
|
+
"no directory containing both .intent/ and .specs/."
|
|
1056
|
+
],
|
|
1057
|
+
engine_id=self.engine_id,
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
if check_type == "all_rules_mapped":
|
|
1061
|
+
return _check_all_rules_mapped(repo_root, check_type)
|
|
1062
|
+
if check_type == "namespace_has_drainer":
|
|
1063
|
+
return await _check_namespace_has_drainer(repo_root, check_type, params)
|
|
1064
|
+
if check_type == "namespace_manifest_completeness":
|
|
1065
|
+
return _check_namespace_manifest_completeness(repo_root, check_type)
|
|
1066
|
+
if check_type == "fs_operations_completeness":
|
|
1067
|
+
return _check_fs_operations_completeness(repo_root, check_type)
|
|
1068
|
+
|
|
1069
|
+
return EngineResult(
|
|
1070
|
+
ok=False,
|
|
1071
|
+
message=f"artifact_gate[{check_type}]: dispatch fall-through.",
|
|
1072
|
+
violations=[f"Unknown governance check_type '{check_type}'"],
|
|
1073
|
+
engine_id=self.engine_id,
|
|
1074
|
+
)
|
|
1075
|
+
|
|
1076
|
+
# -------------------------------------------------------------------------
|
|
1077
|
+
# Context-level dispatch (ADR-076 D3)
|
|
1078
|
+
# -------------------------------------------------------------------------
|
|
1079
|
+
|
|
1080
|
+
# ID: 5a8d2f17-3e6b-4f12-9a47-c1b8e0d5f3a9
|
|
1081
|
+
async def verify_context(
|
|
1082
|
+
self, context: AuditorContext, params: dict[str, Any]
|
|
1083
|
+
) -> list[AuditFinding]:
|
|
1084
|
+
"""Dispatch the six repo-level check_types over AuditorContext.
|
|
1085
|
+
|
|
1086
|
+
ADR-076 D3. The three vocabulary checks and three governance
|
|
1087
|
+
meta-checks (ADR-066/072/075) walk the whole repository — they
|
|
1088
|
+
ignore ``file_path`` in their per-file form. This entry point
|
|
1089
|
+
runs them once per audit with ``context.repo_path`` as the
|
|
1090
|
+
repo root, instead of forcing the dispatcher to walk a synthetic
|
|
1091
|
+
file just so ``verify(file_path, ...)`` could parent-traverse
|
|
1092
|
+
back to the repo root.
|
|
1093
|
+
|
|
1094
|
+
Returns ``list[AuditFinding]`` (the rule_executor contract for
|
|
1095
|
+
context-level), not ``EngineResult``. ``rule_executor`` overwrites
|
|
1096
|
+
severity per the rule's enforcement; check_id is derived from the
|
|
1097
|
+
check_type so the audit verdict surfaces the right rule.
|
|
1098
|
+
"""
|
|
1099
|
+
check_type = params.get("check_type")
|
|
1100
|
+
if check_type is None:
|
|
1101
|
+
return [
|
|
1102
|
+
AuditFinding(
|
|
1103
|
+
check_id=f"{self.engine_id}.error",
|
|
1104
|
+
severity=AuditSeverity.BLOCK,
|
|
1105
|
+
message=(
|
|
1106
|
+
f"{self.engine_id}.verify_context invoked without "
|
|
1107
|
+
"params['check_type']."
|
|
1108
|
+
),
|
|
1109
|
+
file_path="none",
|
|
1110
|
+
)
|
|
1111
|
+
]
|
|
1112
|
+
|
|
1113
|
+
repo_root = context.repo_path
|
|
1114
|
+
|
|
1115
|
+
if check_type in _VOCABULARY_CHECK_TYPES:
|
|
1116
|
+
if check_type == "vocabulary_projection_consistency":
|
|
1117
|
+
result = _check_projection_consistency(repo_root, check_type)
|
|
1118
|
+
elif check_type == "vocabulary_canonical_format":
|
|
1119
|
+
result = _check_canonical_format(repo_root, check_type)
|
|
1120
|
+
elif check_type == "vocabulary_authoritative_paths":
|
|
1121
|
+
result = _check_authoritative_paths(repo_root, check_type)
|
|
1122
|
+
else:
|
|
1123
|
+
result = EngineResult(
|
|
1124
|
+
ok=False,
|
|
1125
|
+
message=f"{self.engine_id}[{check_type}]: dispatch fall-through.",
|
|
1126
|
+
violations=[f"Unknown vocabulary check_type '{check_type}'"],
|
|
1127
|
+
engine_id=self.engine_id,
|
|
1128
|
+
)
|
|
1129
|
+
elif check_type in _GOVERNANCE_CHECK_TYPES:
|
|
1130
|
+
if check_type == "all_rules_mapped":
|
|
1131
|
+
result = _check_all_rules_mapped(repo_root, check_type)
|
|
1132
|
+
elif check_type == "namespace_has_drainer":
|
|
1133
|
+
merged_params = {**params, "_context": context}
|
|
1134
|
+
result = await _check_namespace_has_drainer(
|
|
1135
|
+
repo_root, check_type, merged_params
|
|
1136
|
+
)
|
|
1137
|
+
elif check_type == "namespace_manifest_completeness":
|
|
1138
|
+
result = _check_namespace_manifest_completeness(repo_root, check_type)
|
|
1139
|
+
elif check_type == "fs_operations_completeness":
|
|
1140
|
+
result = _check_fs_operations_completeness(repo_root, check_type)
|
|
1141
|
+
else:
|
|
1142
|
+
result = EngineResult(
|
|
1143
|
+
ok=False,
|
|
1144
|
+
message=f"{self.engine_id}[{check_type}]: dispatch fall-through.",
|
|
1145
|
+
violations=[f"Unknown governance check_type '{check_type}'"],
|
|
1146
|
+
engine_id=self.engine_id,
|
|
1147
|
+
)
|
|
1148
|
+
else:
|
|
1149
|
+
return [
|
|
1150
|
+
AuditFinding(
|
|
1151
|
+
check_id=f"{self.engine_id}.{check_type}.error",
|
|
1152
|
+
severity=AuditSeverity.BLOCK,
|
|
1153
|
+
message=(
|
|
1154
|
+
f"{self.engine_id}.verify_context received non-context-level "
|
|
1155
|
+
f"check_type '{check_type}'. Per-file check_types route "
|
|
1156
|
+
"through verify(file_path, ...)."
|
|
1157
|
+
),
|
|
1158
|
+
file_path="none",
|
|
1159
|
+
)
|
|
1160
|
+
]
|
|
1161
|
+
|
|
1162
|
+
return _result_to_findings(result, check_type, self.engine_id)
|
|
1163
|
+
|
|
1164
|
+
# The actual vocabulary check implementations live as module-level
|
|
1165
|
+
# functions (_check_projection_consistency, _check_canonical_format,
|
|
1166
|
+
# _check_authoritative_paths, _validate_authoritative_path, _vocab_result)
|
|
1167
|
+
# above the class declaration. Keeping them out of the class keeps it
|
|
1168
|
+
# under the modularity.class_too_large threshold.
|