icdev 0.0.3__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.
- args/agent_config.yaml +113 -0
- args/audit_regimes/cisa_sbd.json +381 -0
- args/audit_regimes/cmmc_l2.json +906 -0
- args/audit_regimes/dod_cssp.json +393 -0
- args/audit_regimes/dodi_5000_87.json +297 -0
- args/audit_regimes/fedramp_moderate.json +650 -0
- args/audit_regimes/ieee_1012.json +373 -0
- args/audit_regimes/nist_800_171.json +624 -0
- args/audit_regimes/nist_800_53.json +907 -0
- args/cloudforge_blueprints/aws_commercial.yaml +29 -0
- args/cloudforge_blueprints/aws_govcloud_il4.yaml +34 -0
- args/cloudforge_blueprints/aws_govcloud_il5.yaml +38 -0
- args/cloudforge_blueprints/azure_commercial.yaml +28 -0
- args/cloudforge_blueprints/azure_gov_il4.yaml +32 -0
- args/cloudforge_blueprints/azure_gov_il5.yaml +36 -0
- args/cloudforge_blueprints/gcp_commercial.yaml +28 -0
- args/cloudforge_blueprints/oci_commercial.yaml +28 -0
- args/cloudforge_config.yaml +231 -0
- args/cloudforge_runbook_templates/backup_verify.yaml +98 -0
- args/cloudforge_runbook_templates/dr_failover.yaml +107 -0
- args/cloudforge_runbook_templates/health_check.yaml +97 -0
- args/cloudforge_runbook_templates/incident_response.yaml +101 -0
- args/cloudforge_runbook_templates/migration_cutover.yaml +105 -0
- args/cloudforge_runbook_templates/patch_rollout.yaml +92 -0
- args/cloudforge_runbook_templates/zone_provision.yaml +93 -0
- args/code_pattern_config.yaml +151 -0
- args/code_quality_config.yaml +47 -0
- args/compliance_config.yaml +17 -0
- args/control_inheritance.yaml +177 -0
- args/csp_mcp_config.yaml +41 -0
- args/cui_markings.yaml +35 -0
- args/databridge_config.yaml +232 -0
- args/db_config.yaml +116 -0
- args/decision_tables/agent_trust_decision.yaml +143 -0
- args/decision_tables/ato_boundary_impact.yaml +132 -0
- args/decision_tables/deployment_approval.yaml +152 -0
- args/degradation_matrix.yaml +163 -0
- args/devsecops_config.yaml +286 -0
- args/endpoint_security_config.yaml +207 -0
- args/exit_criteria.yaml +102 -0
- args/feature_flags.yaml +235 -0
- args/file_access_tiers.yaml +88 -0
- args/forge_studio/blueprint_config.yaml +27 -0
- args/forge_studio/component_catalog.json +411 -0
- args/forge_studio/workflow_templates.yaml +103 -0
- args/govcon_config.yaml +41 -0
- args/harness_config.yaml +67 -0
- args/innovation_config.yaml +321 -0
- args/knowledge_graph_config.yaml +113 -0
- args/llm_config.yaml +222 -0
- args/marketplace_config.yaml +260 -0
- args/monitoring_config.yaml +127 -0
- args/mosa_config.yaml +190 -0
- args/observability_tracing_config.yaml +170 -0
- args/owasp_agentic_config.yaml +171 -0
- args/pipeline_gates.yaml +197 -0
- args/project_defaults.yaml +235 -0
- args/prompt_chains.yaml +163 -0
- args/rag_config.yaml +167 -0
- args/research_config.yaml +89 -0
- args/resilience_config.yaml +197 -0
- args/ricoas_config.yaml +191 -0
- args/security_gates.yaml +763 -0
- args/storage_config.yaml +63 -0
- args/writeguard_config.yaml +131 -0
- args/zta_config.yaml +247 -0
- context/__init__.py +6 -0
- context/agent/__init__.py +6 -0
- context/agent/response_schemas/__init__.py +6 -0
- context/agent/response_schemas/debate_position.json +46 -0
- context/agent/response_schemas/fitness_scorecard.json +74 -0
- context/agent/response_schemas/review_decision.json +39 -0
- context/agent/response_schemas/task_decomposition.json +82 -0
- context/agent/response_schemas/veto_decision.json +40 -0
- context/agentic/__init__.py +6 -0
- context/agentic/architecture_patterns.md +269 -0
- context/agentic/capability_registry.yaml +223 -0
- context/agentic/csp_integration.md +30 -0
- context/agentic/csp_mcp_registry.yaml +280 -0
- context/agentic/fitness_rubric.md +56 -0
- context/agentic/governance_baseline.md +205 -0
- context/ci/__init__.py +6 -0
- context/ci/worktree_templates.json +44 -0
- context/cloud/__init__.py +6 -0
- context/cloud/csp_service_registry.json +739 -0
- context/compliance/__init__.py +6 -0
- context/compliance/ai_rmf_crosswalk.yaml +226 -0
- context/compliance/atlas_mitigations.json +293 -0
- context/compliance/atlas_techniques.json +833 -0
- context/compliance/cisa_sbd_requirements.json +477 -0
- context/compliance/cjis_security_policy.json +522 -0
- context/compliance/cmmc_practices.json +2494 -0
- context/compliance/cmmc_report_template.md +142 -0
- context/compliance/cnssi_1253_overlay.json +109 -0
- context/compliance/control_crosswalk.json +1914 -0
- context/compliance/control_families/__init__.py +6 -0
- context/compliance/csp_certifications.json +251 -0
- context/compliance/cssp_report_template.md +193 -0
- context/compliance/cui_templates/__init__.py +6 -0
- context/compliance/cui_templates/banner_block.txt +4 -0
- context/compliance/cui_templates/code_header.txt +8 -0
- context/compliance/cui_templates/document_template.md +35 -0
- context/compliance/data_type_framework_map.json +321 -0
- context/compliance/data_type_registry.json +147 -0
- context/compliance/dod_cssp_8530.json +463 -0
- context/compliance/eu_ai_act_annex_iii.json +108 -0
- context/compliance/export_templates/__init__.py +6 -0
- context/compliance/export_templates/emass_controls.csv.j2 +4 -0
- context/compliance/export_templates/evidence_package.md.j2 +39 -0
- context/compliance/export_templates/executive_summary.md.j2 +55 -0
- context/compliance/export_templates/poam_tracking.csv.j2 +4 -0
- context/compliance/fedramp_20x_ksi_schemas.json +133 -0
- context/compliance/fedramp_high_baseline.json +4370 -0
- context/compliance/fedramp_moderate_baseline.json +2183 -0
- context/compliance/fedramp_report_template.md +181 -0
- context/compliance/fips_200_areas.json +362 -0
- context/compliance/gao_ai_accountability.json +262 -0
- context/compliance/hipaa_security_rule.json +720 -0
- context/compliance/hitrust_csf_v11.json +930 -0
- context/compliance/impact_level_profiles.json +251 -0
- context/compliance/incident_response_template.md +1110 -0
- context/compliance/iso27001_2022_controls.json +750 -0
- context/compliance/iso27001_nist_bridge.json +382 -0
- context/compliance/iso42001_controls.json +254 -0
- context/compliance/ivv_checklist_template.md +80 -0
- context/compliance/ivv_report_template.md +116 -0
- context/compliance/ivv_requirements.json +372 -0
- context/compliance/mosa_crosswalk.json +327 -0
- context/compliance/mosa_framework.json +250 -0
- context/compliance/narrative_templates/AC.md.j2 +101 -0
- context/compliance/narrative_templates/AU.md.j2 +106 -0
- context/compliance/narrative_templates/IA.md.j2 +104 -0
- context/compliance/narrative_templates/SC.md.j2 +102 -0
- context/compliance/narrative_templates/SI.md.j2 +111 -0
- context/compliance/narrative_templates/__init__.py +6 -0
- context/compliance/narrative_templates/default.md.j2 +50 -0
- context/compliance/narrative_templates/executive_summary.j2 +27 -0
- context/compliance/narrative_templates/poam_milestone.j2 +19 -0
- context/compliance/narrative_templates/ssp_section.j2 +11 -0
- context/compliance/nist_800_171_controls.json +1552 -0
- context/compliance/nist_800_207_crosswalk.json +399 -0
- context/compliance/nist_800_207_zta.json +258 -0
- context/compliance/nist_800_53.json +324 -0
- context/compliance/nist_ai_600_1_genai.json +326 -0
- context/compliance/nist_ai_rmf.json +206 -0
- context/compliance/nist_sp_800_60_types.json +1667 -0
- context/compliance/omb_m25_21_high_impact_ai.json +248 -0
- context/compliance/omb_m26_04_unbiased_ai.json +262 -0
- context/compliance/owasp_agentic_asi.json +133 -0
- context/compliance/owasp_agentic_threats.json +285 -0
- context/compliance/owasp_llm_top10.json +274 -0
- context/compliance/pci_dss_v4.json +510 -0
- context/compliance/poam_template.md +117 -0
- context/compliance/safeai_controls.json +512 -0
- context/compliance/sbd_report_template.md +77 -0
- context/compliance/siem_config_templates/__init__.py +6 -0
- context/compliance/siem_config_templates/filebeat.yml +213 -0
- context/compliance/siem_config_templates/log_sources.json +208 -0
- context/compliance/soc2_trust_criteria.json +661 -0
- context/compliance/ssp_template.md +432 -0
- context/compliance/stig_templates/__init__.py +6 -0
- context/compliance/stig_templates/webapp_stig.json +139 -0
- context/compliance/xai_requirements.json +108 -0
- context/dashboard/__init__.py +6 -0
- context/dashboard/nlq_examples.json +50 -0
- context/dashboard/schema_descriptions.json +23 -0
- context/icdev_methodology.md +100 -0
- context/integration/__init__.py +6 -0
- context/integration/approval_workflows.json +32 -0
- context/integration/gitlab_field_mappings.json +33 -0
- context/integration/jira_field_mappings.json +32 -0
- context/integration/reqif_export_schema.json +23 -0
- context/integration/servicenow_field_mappings.json +22 -0
- context/languages/__init__.py +6 -0
- context/languages/framework_patterns.json +205 -0
- context/languages/language_registry.json +279 -0
- context/llm/__init__.py +6 -0
- context/llm/example_provider.py +89 -0
- context/marketplace/assets/writeguard-core.yaml +100 -0
- context/marketplace/assets/writeguard-govcon.yaml +45 -0
- context/marketplace/assets/writeguard-style-guides.yaml +44 -0
- context/mbse/__init__.py +6 -0
- context/mbse/des_report_template.md +162 -0
- context/mbse/des_requirements.json +411 -0
- context/mbse/digital_thread_patterns.json +403 -0
- context/mbse/reqif_schema.json +280 -0
- context/mbse/sysml_element_types.json +432 -0
- context/oscal/NIST_SP-800-53_rev5_catalog.json +254987 -0
- context/oscal/README.md +43 -0
- context/patterns/__init__.py +6 -0
- context/profiles/__init__.py +6 -0
- context/profiles/dod_baseline_v1.yaml +145 -0
- context/profiles/fedramp_baseline_v1.yaml +143 -0
- context/profiles/financial_baseline_v1.yaml +142 -0
- context/profiles/healthcare_baseline_v1.yaml +135 -0
- context/profiles/law_enforcement_v1.yaml +129 -0
- context/profiles/startup_v1.yaml +134 -0
- context/rag/source_mappings.json +42 -0
- context/requirements/__init__.py +6 -0
- context/requirements/ambiguity_patterns.json +97 -0
- context/requirements/boundary_impact_rules.json +123 -0
- context/requirements/default_constitutions.json +67 -0
- context/requirements/document_extraction_rules.json +58 -0
- context/requirements/gap_patterns.json +108 -0
- context/requirements/readiness_rubric.json +78 -0
- context/requirements/red_alternative_patterns.json +210 -0
- context/requirements/safe_templates.json +72 -0
- context/requirements/spec_quality_checklist.json +122 -0
- context/research/regulatory_registry.json +114 -0
- context/research/verticals/cybersecurity.json +127 -0
- context/research/verticals/defense.json +104 -0
- context/research/verticals/fintech.json +125 -0
- context/research/verticals/healthcare.json +118 -0
- context/research/verticals/logistics.json +117 -0
- context/research/verticals/trading.json +145 -0
- context/simulation/__init__.py +6 -0
- context/simulation/architecture_patterns.json +36 -0
- context/simulation/coa_templates.json +38 -0
- context/simulation/cost_models.json +23 -0
- context/simulation/risk_categories.json +46 -0
- context/supply_chain/__init__.py +6 -0
- context/supply_chain/isa_templates.json +129 -0
- context/supply_chain/nist_800_161_controls.json +247 -0
- context/supply_chain/scrm_risk_matrix.json +147 -0
- context/templates/__init__.py +6 -0
- context/templates/ansible/__init__.py +6 -0
- context/templates/ansible/playbooks/__init__.py +6 -0
- context/templates/ansible/roles/__init__.py +6 -0
- context/templates/gitlab_ci/__init__.py +6 -0
- context/templates/grafana/__init__.py +6 -0
- context/templates/kubernetes/__init__.py +6 -0
- context/templates/project/__init__.py +6 -0
- context/templates/project/api/__init__.py +6 -0
- context/templates/project/cli/__init__.py +6 -0
- context/templates/project/data_pipeline/__init__.py +6 -0
- context/templates/project/iac/__init__.py +6 -0
- context/templates/project/javascript_frontend/__init__.py +6 -0
- context/templates/project/javascript_frontend/src/__init__.py +6 -0
- context/templates/project/javascript_frontend/tests/__init__.py +6 -0
- context/templates/project/microservice/__init__.py +6 -0
- context/templates/project/python_backend/__init__.py +6 -0
- context/templates/project/python_backend/src/__init__.py +6 -0
- context/templates/project/python_backend/tests/__init__.py +6 -0
- context/templates/project/python_backend/tests/features/__init__.py +6 -0
- context/templates/project/python_backend/tests/steps/__init__.py +6 -0
- context/templates/terraform/__init__.py +6 -0
- context/templates/terraform/govcloud_base/__init__.py +6 -0
- context/templates/terraform/modules/__init__.py +6 -0
- context/tone/__init__.py +6 -0
- context/writing/grammar_rules/common_errors.json +306 -0
- context/writing/grammar_rules/govcon_vocabulary.json +113 -0
- context/writing/style_guides/academic.yaml +43 -0
- context/writing/style_guides/business.yaml +42 -0
- context/writing/style_guides/government.yaml +59 -0
- context/writing/style_guides/proposal.yaml +58 -0
- context/writing/style_guides/technical.yaml +43 -0
- docs/adr/README.md +66 -0
- docs/adr/connector-forge-decisions.md +318 -0
- docs/adr/core-decisions.md +289 -0
- docs/adr/db-decisions.md +94 -0
- docs/adr/harness-decisions.md +122 -0
- docs/adr/innovation-decisions.md +262 -0
- docs/adr/marketplace-decisions.md +109 -0
- docs/adr/sbd-decisions.md +109 -0
- docs/adr/scale-engine-decisions.md +108 -0
- docs/adr/writeguard-decisions.md +136 -0
- docs/architecture/bounded-contexts.md +1032 -0
- docs/features/phase-65-writeguard.md +139 -0
- docs/features/phase-66-marketplace-commerce.md +79 -0
- docs/features/phase-67-knowledge-ingestion-rag-autodraft.md +97 -0
- docs/features/phase-68-enhanced-autodraft-pipeline.md +109 -0
- docs/features/phase-69-proposalai-marketplace-module.md +131 -0
- docs/features/phase-70-databridge.md +214 -0
- docs/features/phase-71-databridge-messaging.md +102 -0
- docs/implementation-plan-architecture-evolution.md +614 -0
- docs/marketplace/CONTRIBUTING.md +124 -0
- docs/marketplace/module_manifest_schema.yaml +83 -0
- docs/research/ai-architecture-patterns-2024-2026.md +1236 -0
- docs/research/app-builder-platform-analysis.md +582 -0
- docs/research/architecture-patterns-c4-ddd-agentic.md +871 -0
- docs/research/flowable-boat-competitive-analysis.md +426 -0
- docs/research/modern-dev-practices-2024-2026.md +1615 -0
- docs/research/secure-by-design-cloudyrion-adaptation.md +270 -0
- goals/agent_management.md +144 -0
- goals/ai_accountability.md +90 -0
- goals/ai_narratives.md +79 -0
- goals/ai_transparency.md +76 -0
- goals/ato_simulator.md +78 -0
- goals/audit_engine.md +177 -0
- goals/bite_sized_plans.md +225 -0
- goals/boundary_supply_chain.md +206 -0
- goals/brainstorming_gate.md +186 -0
- goals/build_app.md +604 -0
- goals/cato_live_evidence.md +77 -0
- goals/cloudforge.md +106 -0
- goals/code_intelligence.md +197 -0
- goals/compliance_workflow.md +858 -0
- goals/connector_forge.md +133 -0
- goals/databridge.md +128 -0
- goals/deploy_workflow.md +390 -0
- goals/developer_scorecard.md +78 -0
- goals/devsecops_workflow.md +408 -0
- goals/firmware_sbom.md +79 -0
- goals/forge_hub.md +78 -0
- goals/golden_path.md +77 -0
- goals/harness_engineering.md +91 -0
- goals/integration_testing.md +189 -0
- goals/knowledge_graph.md +128 -0
- goals/maintenance_audit.md +196 -0
- goals/manifest.md +50 -0
- goals/monitoring.md +126 -0
- goals/mosa_workflow.md +463 -0
- goals/multi_agent_orchestration.md +68 -0
- goals/observability_traceability_xai.md +154 -0
- goals/owasp_agentic_security.md +395 -0
- goals/pr_intelligence.md +78 -0
- goals/requirements_intake.md +213 -0
- goals/secure_by_design.md +135 -0
- goals/security_scan.md +381 -0
- goals/self_healing.md +120 -0
- goals/simulation_engine.md +111 -0
- goals/subagent_review.md +205 -0
- goals/systematic_debugging.md +257 -0
- goals/tdd_workflow.md +403 -0
- goals/template_exchange.md +77 -0
- goals/thread_heatmap.md +77 -0
- goals/threat_modeler.md +77 -0
- goals/verification_iron_law.md +192 -0
- goals/vsm_dashboard.md +76 -0
- goals/writeguard.md +89 -0
- goals/zero_trust_architecture.md +403 -0
- hardprompts/__init__.py +6 -0
- hardprompts/agent/__init__.py +6 -0
- hardprompts/agent/agentic_architect.md +100 -0
- hardprompts/agent/debate_prompt.md +32 -0
- hardprompts/agent/fitness_evaluation.md +48 -0
- hardprompts/agent/governance_review.md +214 -0
- hardprompts/agent/reviewer_prompt.md +34 -0
- hardprompts/agent/skill_design.md +172 -0
- hardprompts/agent/task_decomposition.md +275 -0
- hardprompts/agent/veto_check_prompt.md +33 -0
- hardprompts/architect/__init__.py +6 -0
- hardprompts/architect/api_design.md +283 -0
- hardprompts/architect/data_model.md +277 -0
- hardprompts/architect/system_design.md +180 -0
- hardprompts/builder/__init__.py +6 -0
- hardprompts/builder/code_generation.md +59 -0
- hardprompts/builder/refactor.md +58 -0
- hardprompts/builder/scaffold_project.md +69 -0
- hardprompts/builder/test_generation.md +87 -0
- hardprompts/ci/__init__.py +6 -0
- hardprompts/ci/worktree_setup.md +35 -0
- hardprompts/compliance/__init__.py +6 -0
- hardprompts/compliance/cmmc_assessment.md +63 -0
- hardprompts/compliance/cssp_assessment.md +75 -0
- hardprompts/compliance/cui_marking.md +86 -0
- hardprompts/compliance/fedramp_assessment.md +55 -0
- hardprompts/compliance/ivv_assessment.md +96 -0
- hardprompts/compliance/poam_generation.md +57 -0
- hardprompts/compliance/sbd_assessment.md +101 -0
- hardprompts/compliance/security_categorization.md +74 -0
- hardprompts/compliance/ssp_generation.md +56 -0
- hardprompts/compliance/stig_evaluation.md +63 -0
- hardprompts/dashboard/__init__.py +6 -0
- hardprompts/dashboard/nlq_system_prompt.md +26 -0
- hardprompts/infra/__init__.py +6 -0
- hardprompts/infra/k8s_manifests.md +118 -0
- hardprompts/infra/pipeline_generation.md +160 -0
- hardprompts/infra/terraform_generation.md +92 -0
- hardprompts/integration/__init__.py +6 -0
- hardprompts/integration/approval_review.md +17 -0
- hardprompts/integration/jira_mapping.md +25 -0
- hardprompts/integration/servicenow_mapping.md +14 -0
- hardprompts/knowledge/__init__.py +6 -0
- hardprompts/knowledge/pattern_detection.md +73 -0
- hardprompts/knowledge/recommendation_engine.md +90 -0
- hardprompts/knowledge/root_cause_analysis.md +91 -0
- hardprompts/maintenance/__init__.py +6 -0
- hardprompts/maintenance/maintenance_assessment.md +82 -0
- hardprompts/mbse/__init__.py +6 -0
- hardprompts/mbse/digital_thread.md +67 -0
- hardprompts/mbse/model_import.md +62 -0
- hardprompts/mbse/model_to_code.md +65 -0
- hardprompts/modernization/__init__.py +6 -0
- hardprompts/modernization/legacy_analysis.md +93 -0
- hardprompts/modernization/migration_planning.md +150 -0
- hardprompts/modernization/seven_r_assessment.md +107 -0
- hardprompts/proposal_draft.md +53 -0
- hardprompts/rag_citation.md +12 -0
- hardprompts/rag_rerank.md +31 -0
- hardprompts/requirements/__init__.py +6 -0
- hardprompts/requirements/bdd_generation.md +35 -0
- hardprompts/requirements/clarification_prioritization.md +29 -0
- hardprompts/requirements/decomposition.md +60 -0
- hardprompts/requirements/document_extraction.md +45 -0
- hardprompts/requirements/gap_detection.md +70 -0
- hardprompts/requirements/intake_conversation.md +101 -0
- hardprompts/requirements/readiness_assessment.md +39 -0
- hardprompts/requirements/spec_quality.md +33 -0
- hardprompts/requirements/traceability_analysis.md +23 -0
- hardprompts/security/__init__.py +6 -0
- hardprompts/security/endpoint_security.md +78 -0
- hardprompts/security/threat_model.md +70 -0
- hardprompts/security/vulnerability_assessment.md +81 -0
- hardprompts/simulation/__init__.py +6 -0
- hardprompts/simulation/architecture_impact.md +27 -0
- hardprompts/simulation/coa_alternative.md +27 -0
- hardprompts/simulation/coa_generation.md +25 -0
- hardprompts/simulation/compliance_impact.md +28 -0
- hardprompts/simulation/cost_estimation.md +33 -0
- hardprompts/simulation/risk_assessment.md +28 -0
- hardprompts/translation/code_translation.md +68 -0
- hardprompts/translation/dependency_suggestion.md +44 -0
- hardprompts/translation/test_translation.md +64 -0
- hardprompts/translation/translation_repair.md +59 -0
- icdev-0.0.3.dist-info/METADATA +909 -0
- icdev-0.0.3.dist-info/RECORD +1214 -0
- icdev-0.0.3.dist-info/WHEEL +5 -0
- icdev-0.0.3.dist-info/entry_points.txt +9 -0
- icdev-0.0.3.dist-info/licenses/LICENSE +201 -0
- icdev-0.0.3.dist-info/licenses/NOTICE +11 -0
- icdev-0.0.3.dist-info/top_level.txt +7 -0
- memory/MEMORY.md +52 -0
- memory/logs/2026-02-14.md +17 -0
- memory/logs/2026-03-03.md +2 -0
- memory/logs/__init__.py +1 -0
- tools/a2a/icdev_callback_client.py +210 -0
- tools/agent/cards/architect_card.json +29 -0
- tools/agent/cards/builder_card.json +34 -0
- tools/agent/cards/compliance_card.json +29 -0
- tools/agent/cards/connector_forge_card.json +49 -0
- tools/agent/cards/devsecops_zta_card.json +24 -0
- tools/agent/cards/knowledge_card.json +29 -0
- tools/agent/cards/monitor_card.json +29 -0
- tools/agent/cards/orchestrator_card.json +29 -0
- tools/agent/cards/requirements_analyst_card.json +24 -0
- tools/agent/cards/security_card.json +29 -0
- tools/agent/cards/simulation_card.json +24 -0
- tools/agent/cards/supply_chain_card.json +24 -0
- tools/analysis/__init__.py +1 -0
- tools/analysis/code_analyzer.py +770 -0
- tools/analysis/runtime_feedback.py +379 -0
- tools/analytics/__init__.py +2 -0
- tools/analytics/scorecard.py +538 -0
- tools/analytics/vsm_engine.py +612 -0
- tools/architecture/__init__.py +2 -0
- tools/architecture/adr_extractor.py +393 -0
- tools/audit/__init__.py +1 -0
- tools/audit/audit_logger.py +199 -0
- tools/audit/audit_query.py +153 -0
- tools/audit/decision_recorder.py +73 -0
- tools/audit_engine/__init__.py +12 -0
- tools/audit_engine/ai_advisor.py +906 -0
- tools/audit_engine/cli.py +286 -0
- tools/audit_engine/comparator.py +305 -0
- tools/audit_engine/eject_scaffolder.py +399 -0
- tools/audit_engine/engine.py +614 -0
- tools/audit_engine/git_fetcher.py +341 -0
- tools/audit_engine/regime_loader.py +200 -0
- tools/audit_engine/regime_updater.py +325 -0
- tools/audit_engine/report_card.py +289 -0
- tools/audit_engine/scanner.py +684 -0
- tools/audit_engine/self_heal.py +1042 -0
- tools/ci/__init__.py +2 -0
- tools/ci/connectors/__init__.py +2 -0
- tools/ci/connectors/base_connector.py +80 -0
- tools/ci/connectors/connector_registry.py +188 -0
- tools/ci/connectors/mattermost_connector.py +159 -0
- tools/ci/connectors/slack_connector.py +197 -0
- tools/ci/core/__init__.py +2 -0
- tools/ci/core/air_gap_detector.py +115 -0
- tools/ci/core/comment_handler.py +192 -0
- tools/ci/core/conversation_manager.py +480 -0
- tools/ci/core/event_envelope.py +500 -0
- tools/ci/core/event_router.py +444 -0
- tools/ci/core/failure_parser.py +397 -0
- tools/ci/core/recovery_engine.py +527 -0
- tools/ci/gate_enforcer.py +361 -0
- tools/ci/modules/__init__.py +2 -0
- tools/ci/modules/agent.py +271 -0
- tools/ci/modules/git_ops.py +175 -0
- tools/ci/modules/state.py +117 -0
- tools/ci/modules/vcs.py +303 -0
- tools/ci/modules/workflow_ops.py +295 -0
- tools/ci/modules/worktree.py +337 -0
- tools/ci/pipeline_config_generator.py +558 -0
- tools/ci/pr_intelligence.py +485 -0
- tools/ci/triggers/__init__.py +2 -0
- tools/ci/triggers/gitlab_task_monitor.py +327 -0
- tools/ci/triggers/poll_trigger.py +237 -0
- tools/ci/triggers/webhook_server.py +356 -0
- tools/ci/workflows/__init__.py +2 -0
- tools/ci/workflows/icdev_build.py +140 -0
- tools/ci/workflows/icdev_comply.py +284 -0
- tools/ci/workflows/icdev_document.py +152 -0
- tools/ci/workflows/icdev_e2e.py +188 -0
- tools/ci/workflows/icdev_patch.py +186 -0
- tools/ci/workflows/icdev_plan.py +202 -0
- tools/ci/workflows/icdev_plan_build.py +41 -0
- tools/ci/workflows/icdev_plan_build_test.py +46 -0
- tools/ci/workflows/icdev_plan_build_test_review.py +47 -0
- tools/ci/workflows/icdev_review.py +126 -0
- tools/ci/workflows/icdev_sdlc.py +261 -0
- tools/ci/workflows/icdev_test.py +240 -0
- tools/cli/__init__.py +1 -0
- tools/cli/output_formatter.py +756 -0
- tools/cloudforge/__init__.py +12 -0
- tools/cloudforge/airgap/__init__.py +2 -0
- tools/cloudforge/airgap/il_classifier.py +70 -0
- tools/cloudforge/airgap/offline_validator.py +42 -0
- tools/cloudforge/airgap/shift_emulator.py +155 -0
- tools/cloudforge/airgap/sneakernet.py +91 -0
- tools/cloudforge/cd_hub/__init__.py +2 -0
- tools/cloudforge/cd_hub/canary_deployer.py +88 -0
- tools/cloudforge/cd_hub/gitops_renderer.py +123 -0
- tools/cloudforge/cd_hub/hub_controller.py +143 -0
- tools/cloudforge/cd_hub/pipeline_bridge.py +30 -0
- tools/cloudforge/cd_hub/rollback_engine.py +29 -0
- tools/cloudforge/cd_hub/spoke_agent.py +51 -0
- tools/cloudforge/compliance/__init__.py +2 -0
- tools/cloudforge/compliance/ato_accelerator.py +272 -0
- tools/cloudforge/compliance/control_inheritor.py +127 -0
- tools/cloudforge/compliance/evidence_generator.py +129 -0
- tools/cloudforge/compliance/poam_bridge.py +41 -0
- tools/cloudforge/compliance/ssp_bridge.py +52 -0
- tools/cloudforge/compliance/stig_bridge.py +41 -0
- tools/cloudforge/container_forge/__init__.py +2 -0
- tools/cloudforge/container_forge/bigbang_renderer.py +85 -0
- tools/cloudforge/container_forge/hardener.py +169 -0
- tools/cloudforge/container_forge/image_scanner_bridge.py +33 -0
- tools/cloudforge/container_forge/runtime_policy.py +87 -0
- tools/cloudforge/container_forge/sbom_bridge.py +42 -0
- tools/cloudforge/finops/__init__.py +2 -0
- tools/cloudforge/finops/anomaly_detector.py +78 -0
- tools/cloudforge/finops/budget_tracker.py +96 -0
- tools/cloudforge/finops/chargeback.py +69 -0
- tools/cloudforge/finops/cost_collector.py +141 -0
- tools/cloudforge/finops/optimizer.py +55 -0
- tools/cloudforge/hybrid/__init__.py +2 -0
- tools/cloudforge/hybrid/connection_manager.py +141 -0
- tools/cloudforge/hybrid/dns_federator.py +56 -0
- tools/cloudforge/hybrid/health_monitor.py +108 -0
- tools/cloudforge/hybrid/identity_federator.py +53 -0
- tools/cloudforge/hybrid/network_bridge.py +68 -0
- tools/cloudforge/hybrid/topology_manager.py +147 -0
- tools/cloudforge/hybrid/workload_abstractor.py +92 -0
- tools/cloudforge/iac/__init__.py +2 -0
- tools/cloudforge/iac/drift_detector.py +154 -0
- tools/cloudforge/iac/module_library.py +265 -0
- tools/cloudforge/iac/opentofu_adapter.py +89 -0
- tools/cloudforge/iac/pulumi_renderer.py +292 -0
- tools/cloudforge/iac/state_backend.py +146 -0
- tools/cloudforge/iac/terraform_renderer.py +626 -0
- tools/cloudforge/landing_zone/__init__.py +2 -0
- tools/cloudforge/landing_zone/blueprint_loader.py +98 -0
- tools/cloudforge/landing_zone/blueprint_validator.py +113 -0
- tools/cloudforge/landing_zone/zone_provisioner.py +306 -0
- tools/cloudforge/landing_zone/zone_state.py +143 -0
- tools/cloudforge/mbse_thread/__init__.py +2 -0
- tools/cloudforge/mbse_thread/ato_thread_weaver.py +111 -0
- tools/cloudforge/mbse_thread/control_tracer.py +68 -0
- tools/cloudforge/mbse_thread/system_boundary.py +83 -0
- tools/cloudforge/metastore/__init__.py +2 -0
- tools/cloudforge/metastore/dependency_graph.py +202 -0
- tools/cloudforge/metastore/discovery.py +192 -0
- tools/cloudforge/metastore/registry.py +185 -0
- tools/cloudforge/metastore/rto_tracker.py +92 -0
- tools/cloudforge/metastore/runbook_linker.py +82 -0
- tools/cloudforge/migration/__init__.py +2 -0
- tools/cloudforge/migration/assessor.py +187 -0
- tools/cloudforge/migration/cutover_orchestrator.py +117 -0
- tools/cloudforge/migration/databridge_bridge.py +92 -0
- tools/cloudforge/migration/planner.py +98 -0
- tools/cloudforge/migration/risk_scorer.py +97 -0
- tools/cloudforge/migration/validation_runner.py +45 -0
- tools/cloudforge/migration/workload_inventory.py +107 -0
- tools/cloudforge/provider.py +319 -0
- tools/cloudforge/providers/__init__.py +2 -0
- tools/cloudforge/providers/aws_commercial.py +92 -0
- tools/cloudforge/providers/aws_govcloud.py +229 -0
- tools/cloudforge/providers/aws_secret.py +83 -0
- tools/cloudforge/providers/azure_commercial.py +80 -0
- tools/cloudforge/providers/azure_gov.py +91 -0
- tools/cloudforge/providers/azure_secret.py +71 -0
- tools/cloudforge/providers/gcp.py +102 -0
- tools/cloudforge/providers/oci.py +102 -0
- tools/cloudforge/registry.py +140 -0
- tools/cloudforge/runbooks/__init__.py +2 -0
- tools/cloudforge/runbooks/ai_generator.py +119 -0
- tools/cloudforge/runbooks/dag_validator.py +219 -0
- tools/cloudforge/runbooks/engine.py +470 -0
- tools/cloudforge/runbooks/models.py +99 -0
- tools/cloudforge/runbooks/snippet_library.py +158 -0
- tools/cloudforge/runbooks/template_loader.py +122 -0
- tools/cloudforge/runbooks/visualization.py +108 -0
- tools/cloudforge/siem/__init__.py +2 -0
- tools/cloudforge/siem/alert_rules.py +86 -0
- tools/cloudforge/siem/correlation_engine.py +61 -0
- tools/cloudforge/siem/log_aggregator.py +113 -0
- tools/cloudforge/siem/siem_dashboard_data.py +28 -0
- tools/cloudforge/supply_chain/__init__.py +2 -0
- tools/cloudforge/supply_chain/bridge.py +33 -0
- tools/cloudforge/supply_chain/iac_dependency_scanner.py +36 -0
- tools/cloudforge/supply_chain/provider_trust_scorer.py +54 -0
- tools/compat/__init__.py +21 -0
- tools/compat/cli_harmonizer.py +251 -0
- tools/compat/datetime_utils.py +18 -0
- tools/compat/db_utils.py +190 -0
- tools/compat/platform_utils.py +123 -0
- tools/compliance/__init__.py +1 -0
- tools/compliance/accountability_manager.py +391 -0
- tools/compliance/ai_accountability_audit.py +287 -0
- tools/compliance/ai_impact_assessor.py +267 -0
- tools/compliance/ai_incident_response.py +295 -0
- tools/compliance/ai_inventory_manager.py +233 -0
- tools/compliance/ai_reassessment_scheduler.py +250 -0
- tools/compliance/ai_transparency_audit.py +247 -0
- tools/compliance/atlas_assessor.py +276 -0
- tools/compliance/atlas_report_generator.py +1199 -0
- tools/compliance/base_assessor.py +591 -0
- tools/compliance/cato_live_engine.py +607 -0
- tools/compliance/cato_monitor.py +1371 -0
- tools/compliance/cato_scheduler.py +698 -0
- tools/compliance/cjis_assessor.py +76 -0
- tools/compliance/classification_manager.py +1340 -0
- tools/compliance/cmmc_assessor.py +1478 -0
- tools/compliance/cmmc_report_generator.py +1087 -0
- tools/compliance/compliance_detector.py +452 -0
- tools/compliance/compliance_exporter.py +418 -0
- tools/compliance/compliance_status.py +810 -0
- tools/compliance/control_mapper.py +488 -0
- tools/compliance/crosswalk_engine.py +1208 -0
- tools/compliance/cssp_assessor.py +1032 -0
- tools/compliance/cssp_evidence_collector.py +716 -0
- tools/compliance/cssp_report_generator.py +1103 -0
- tools/compliance/cui_marker.py +387 -0
- tools/compliance/diagram_validator.py +599 -0
- tools/compliance/emass/__init__.py +2 -0
- tools/compliance/emass/emass_client.py +822 -0
- tools/compliance/emass/emass_export.py +758 -0
- tools/compliance/emass/emass_sync.py +807 -0
- tools/compliance/eu_ai_act_classifier.py +193 -0
- tools/compliance/evidence_collector.py +459 -0
- tools/compliance/fairness_assessor.py +310 -0
- tools/compliance/fedramp_20x_ksi_emitter.py +692 -0
- tools/compliance/fedramp_assessor.py +1795 -0
- tools/compliance/fedramp_authorization_packager.py +137 -0
- tools/compliance/fedramp_ksi_generator.py +349 -0
- tools/compliance/fedramp_report_generator.py +1115 -0
- tools/compliance/fips199_categorizer.py +869 -0
- tools/compliance/fips200_validator.py +304 -0
- tools/compliance/firmware_sbom.py +646 -0
- tools/compliance/gao_ai_assessor.py +228 -0
- tools/compliance/gao_evidence_builder.py +302 -0
- tools/compliance/hipaa_assessor.py +78 -0
- tools/compliance/hitrust_assessor.py +49 -0
- tools/compliance/incident_response_plan.py +705 -0
- tools/compliance/inheritance_engine.py +693 -0
- tools/compliance/iso27001_assessor.py +92 -0
- tools/compliance/iso42001_assessor.py +114 -0
- tools/compliance/ivv_assessor.py +2314 -0
- tools/compliance/ivv_report_generator.py +1649 -0
- tools/compliance/model_card_generator.py +291 -0
- tools/compliance/mosa_assessor.py +117 -0
- tools/compliance/multi_regime_assessor.py +441 -0
- tools/compliance/narrative_generator.py +1012 -0
- tools/compliance/narrative_quality_gate.py +701 -0
- tools/compliance/narrative_workflow.py +814 -0
- tools/compliance/nist_800_207_assessor.py +191 -0
- tools/compliance/nist_ai_600_1_assessor.py +185 -0
- tools/compliance/nist_ai_rmf_assessor.py +110 -0
- tools/compliance/nist_lookup.py +244 -0
- tools/compliance/omb_m25_21_assessor.py +225 -0
- tools/compliance/omb_m26_04_assessor.py +185 -0
- tools/compliance/oscal_catalog_adapter.py +395 -0
- tools/compliance/oscal_generator.py +2157 -0
- tools/compliance/oscal_tools.py +1182 -0
- tools/compliance/oscal_validator.py +692 -0
- tools/compliance/owasp_agentic_assessor.py +227 -0
- tools/compliance/owasp_asi_assessor.py +197 -0
- tools/compliance/owasp_llm_assessor.py +245 -0
- tools/compliance/pci_dss_assessor.py +80 -0
- tools/compliance/pi_compliance_tracker.py +1447 -0
- tools/compliance/poam_generator.py +388 -0
- tools/compliance/resolve_marking.py +272 -0
- tools/compliance/sbd_assessor.py +2070 -0
- tools/compliance/sbd_report_generator.py +1223 -0
- tools/compliance/sbom_generator.py +993 -0
- tools/compliance/siem_config_generator.py +661 -0
- tools/compliance/slsa_attestation_generator.py +479 -0
- tools/compliance/soc2_assessor.py +77 -0
- tools/compliance/ssp_generator.py +556 -0
- tools/compliance/stig_checker.py +712 -0
- tools/compliance/swft_evidence_bundler.py +326 -0
- tools/compliance/system_card_generator.py +303 -0
- tools/compliance/template_exchange.py +513 -0
- tools/compliance/traceability_matrix.py +1268 -0
- tools/compliance/universal_classification_manager.py +1159 -0
- tools/compliance/xacta/__init__.py +2 -0
- tools/compliance/xacta/xacta_client.py +438 -0
- tools/compliance/xacta/xacta_export.py +546 -0
- tools/compliance/xacta/xacta_sync.py +322 -0
- tools/compliance/xai_assessor.py +231 -0
- tools/core/__init__.py +2 -0
- tools/core/circuit_breaker.py +353 -0
- tools/core/compliance_sidecar.py +344 -0
- tools/core/container.py +110 -0
- tools/core/errors.py +256 -0
- tools/core/feature_flags.py +311 -0
- tools/core/task_dlq.py +350 -0
- tools/dashboard/__init__.py +2 -0
- tools/dashboard/app.py +6288 -0
- tools/dashboard/templates/agent_evolution.html +287 -0
- tools/dashboard/templates/agents/list.html +71 -0
- tools/dashboard/templates/agents.html +132 -0
- tools/dashboard/templates/architecture.html +289 -0
- tools/dashboard/templates/ato_simulator.html +170 -0
- tools/dashboard/templates/audit_engine.html +844 -0
- tools/dashboard/templates/base.html +236 -0
- tools/dashboard/templates/cato_live.html +116 -0
- tools/dashboard/templates/cloudforge.html +195 -0
- tools/dashboard/templates/cloudforge_finops.html +111 -0
- tools/dashboard/templates/cloudforge_hybrid.html +122 -0
- tools/dashboard/templates/cloudforge_metastore.html +234 -0
- tools/dashboard/templates/cloudforge_migration.html +87 -0
- tools/dashboard/templates/cloudforge_runbooks.html +201 -0
- tools/dashboard/templates/cloudforge_siem.html +94 -0
- tools/dashboard/templates/compliance_accel.html +292 -0
- tools/dashboard/templates/crashes.html +122 -0
- tools/dashboard/templates/databridge.html +305 -0
- tools/dashboard/templates/databridge_analytics.html +195 -0
- tools/dashboard/templates/databridge_mapping.html +345 -0
- tools/dashboard/templates/databridge_messaging.html +321 -0
- tools/dashboard/templates/decisions.html +258 -0
- tools/dashboard/templates/devices.html +151 -0
- tools/dashboard/templates/devsecops_maturity.html +278 -0
- tools/dashboard/templates/edge_ai.html +128 -0
- tools/dashboard/templates/firmware.html +120 -0
- tools/dashboard/templates/firmware_sbom.html +193 -0
- tools/dashboard/templates/forge_hub.html +196 -0
- tools/dashboard/templates/forge_studio.html +379 -0
- tools/dashboard/templates/forge_studio_analytics.html +360 -0
- tools/dashboard/templates/forge_studio_builder.html +1637 -0
- tools/dashboard/templates/forge_studio_compliance.html +310 -0
- tools/dashboard/templates/forge_studio_deploy.html +573 -0
- tools/dashboard/templates/forge_studio_enterprise.html +888 -0
- tools/dashboard/templates/forge_studio_marketplace.html +502 -0
- tools/dashboard/templates/forge_studio_workflow.html +696 -0
- tools/dashboard/templates/golden_path.html +175 -0
- tools/dashboard/templates/govcon.html +280 -0
- tools/dashboard/templates/harness.html +148 -0
- tools/dashboard/templates/index.html +207 -0
- tools/dashboard/templates/intelligence.html +336 -0
- tools/dashboard/templates/knowledge/index.html +190 -0
- tools/dashboard/templates/knowledge_graph.html +739 -0
- tools/dashboard/templates/login.html +51 -0
- tools/dashboard/templates/marketplace.html +336 -0
- tools/dashboard/templates/marketplace_admin.html +247 -0
- tools/dashboard/templates/missions.html +403 -0
- tools/dashboard/templates/narratives.html +154 -0
- tools/dashboard/templates/pr_intelligence.html +151 -0
- tools/dashboard/templates/proposals/detail.html +300 -0
- tools/dashboard/templates/proposals/list.html +52 -0
- tools/dashboard/templates/proposals/sam_detail.html +132 -0
- tools/dashboard/templates/proposals/section_detail.html +375 -0
- tools/dashboard/templates/research.html +222 -0
- tools/dashboard/templates/resilience.html +300 -0
- tools/dashboard/templates/scorecard.html +162 -0
- tools/dashboard/templates/simulator.html +131 -0
- tools/dashboard/templates/template_exchange.html +147 -0
- tools/dashboard/templates/thread_heatmap.html +151 -0
- tools/dashboard/templates/threat_model.html +195 -0
- tools/dashboard/templates/vsm.html +141 -0
- tools/dashboard/templates/writeguard.html +277 -0
- tools/databridge/__init__.py +5 -0
- tools/databridge/agent/__init__.py +2 -0
- tools/databridge/agent/daemon.py +227 -0
- tools/databridge/agent/tunnel.py +101 -0
- tools/databridge/agent/ws_relay.py +91 -0
- tools/databridge/analytics.py +167 -0
- tools/databridge/arrow_pipeline.py +327 -0
- tools/databridge/connection_manager.py +424 -0
- tools/databridge/connector.py +331 -0
- tools/databridge/connectors/__init__.py +2 -0
- tools/databridge/connectors/argocd_connector.py +160 -0
- tools/databridge/connectors/avro_connector.py +203 -0
- tools/databridge/connectors/azure_blob.py +63 -0
- tools/databridge/connectors/cdc_connector.py +205 -0
- tools/databridge/connectors/csv_connector.py +172 -0
- tools/databridge/connectors/datadog_connector.py +153 -0
- tools/databridge/connectors/discord_messaging.py +215 -0
- tools/databridge/connectors/dynamics365.py +151 -0
- tools/databridge/connectors/elasticsearch_connector.py +145 -0
- tools/databridge/connectors/email_base.py +114 -0
- tools/databridge/connectors/excel_connector.py +175 -0
- tools/databridge/connectors/fsspec_base.py +300 -0
- tools/databridge/connectors/gcs.py +53 -0
- tools/databridge/connectors/github_connector.py +138 -0
- tools/databridge/connectors/gitlab_connector.py +132 -0
- tools/databridge/connectors/gmail_connector.py +182 -0
- tools/databridge/connectors/hdfs.py +57 -0
- tools/databridge/connectors/health_base.py +401 -0
- tools/databridge/connectors/hubspot.py +124 -0
- tools/databridge/connectors/imap_connector.py +171 -0
- tools/databridge/connectors/jenkins_connector.py +138 -0
- tools/databridge/connectors/jira_connector.py +86 -0
- tools/databridge/connectors/json_connector.py +184 -0
- tools/databridge/connectors/kafka_connector.py +246 -0
- tools/databridge/connectors/kinesis_connector.py +238 -0
- tools/databridge/connectors/local_fs.py +30 -0
- tools/databridge/connectors/matrix.py +197 -0
- tools/databridge/connectors/mattermost_messaging.py +184 -0
- tools/databridge/connectors/messaging_base.py +172 -0
- tools/databridge/connectors/mssql.py +63 -0
- tools/databridge/connectors/mysql.py +57 -0
- tools/databridge/connectors/netsuite.py +170 -0
- tools/databridge/connectors/o365_mail.py +196 -0
- tools/databridge/connectors/oracle.py +65 -0
- tools/databridge/connectors/pagerduty_connector.py +162 -0
- tools/databridge/connectors/parquet_connector.py +131 -0
- tools/databridge/connectors/postgresql.py +58 -0
- tools/databridge/connectors/s3.py +65 -0
- tools/databridge/connectors/saas_base.py +198 -0
- tools/databridge/connectors/salesforce.py +126 -0
- tools/databridge/connectors/sap.py +89 -0
- tools/databridge/connectors/servicenow.py +60 -0
- tools/databridge/connectors/signal_messaging.py +150 -0
- tools/databridge/connectors/slack_messaging.py +203 -0
- tools/databridge/connectors/smtp_connector.py +126 -0
- tools/databridge/connectors/soap_base.py +258 -0
- tools/databridge/connectors/splunk_connector.py +171 -0
- tools/databridge/connectors/sql_base.py +310 -0
- tools/databridge/connectors/sqlite_connector.py +76 -0
- tools/databridge/connectors/teams.py +148 -0
- tools/databridge/connectors/telegram.py +192 -0
- tools/databridge/connectors/whatsapp.py +137 -0
- tools/databridge/data_profiler.py +99 -0
- tools/databridge/forge/__init__.py +6 -0
- tools/databridge/forge/base_selector.py +150 -0
- tools/databridge/forge/code_generator.py +206 -0
- tools/databridge/forge/community_hub.py +539 -0
- tools/databridge/forge/forge_agent.py +306 -0
- tools/databridge/forge/import_handler.py +133 -0
- tools/databridge/forge/integration_tester.py +127 -0
- tools/databridge/forge/marketplace_publisher.py +164 -0
- tools/databridge/forge/promoter.py +159 -0
- tools/databridge/forge/sandbox_manager.py +257 -0
- tools/databridge/forge/spec_parser.py +358 -0
- tools/databridge/forge/static_validator.py +363 -0
- tools/databridge/forge/templates/__init__.py +591 -0
- tools/databridge/format_converter.py +188 -0
- tools/databridge/mapping_engine.py +348 -0
- tools/databridge/messaging/__init__.py +5 -0
- tools/databridge/messaging/agent_bridge.py +254 -0
- tools/databridge/messaging/message_envelope.py +111 -0
- tools/databridge/messaging/message_logger.py +204 -0
- tools/databridge/messaging/messaging_daemon.py +326 -0
- tools/databridge/messaging/oauth2_manager.py +411 -0
- tools/databridge/pii_detector.py +221 -0
- tools/databridge/registry.py +352 -0
- tools/databridge/relay_server.py +105 -0
- tools/databridge/scale/__init__.py +16 -0
- tools/databridge/scale/backpressure.py +134 -0
- tools/databridge/scale/chunked_pipeline.py +169 -0
- tools/databridge/scale/connection_pool.py +293 -0
- tools/databridge/scale/engine.py +492 -0
- tools/databridge/scale/worker_pool.py +140 -0
- tools/databridge/scale/write_batcher.py +250 -0
- tools/databridge/schema_engine.py +324 -0
- tools/databridge/stream_manager.py +225 -0
- tools/databridge/sync_engine.py +411 -0
- tools/databridge/transforms.py +302 -0
- tools/db/__init__.py +1 -0
- tools/db/backup.py +312 -0
- tools/db/backup_manager.py +832 -0
- tools/db/init_icdev_db.py +7753 -0
- tools/db/init_sparkpilot_db.py +431 -0
- tools/db/migrate.py +177 -0
- tools/db/migrate_innovation_audit.py +165 -0
- tools/db/migration_runner.py +548 -0
- tools/db/migrations/001_baseline/meta.json +9 -0
- tools/db/migrations/001_baseline/up.py +67 -0
- tools/db/migrations/002_memory_enhancements/down.sql +8 -0
- tools/db/migrations/002_memory_enhancements/meta.json +9 -0
- tools/db/migrations/002_memory_enhancements/up.py +119 -0
- tools/db/migrations/003_dev_profiles/meta.json +8 -0
- tools/db/migrations/003_dev_profiles/up.py +93 -0
- tools/db/migrations/004_innovation_engine/down.py +19 -0
- tools/db/migrations/004_innovation_engine/up.py +227 -0
- tools/db/migrations/005_phase_37_ai_security/down.py +19 -0
- tools/db/migrations/005_phase_37_ai_security/up.py +257 -0
- tools/db/migrations/006_phase_36_evolution/down.py +21 -0
- tools/db/migrations/006_phase_36_evolution/up.py +323 -0
- tools/db/migrations/007_phase_38_cloud/down.py +14 -0
- tools/db/migrations/007_phase_38_cloud/up.py +110 -0
- tools/db/migrations/008_phase36_37_integration/up.py +55 -0
- tools/db/migrations/__init__.py +2 -0
- tools/db/pg_migrate.py +642 -0
- tools/db/storage.py +1080 -0
- tools/decisions/__init__.py +2 -0
- tools/decisions/dmn_engine.py +695 -0
- tools/devsecops/__init__.py +2 -0
- tools/devsecops/attestation_manager.py +449 -0
- tools/devsecops/network_segmentation_generator.py +604 -0
- tools/devsecops/pdp_config_generator.py +1246 -0
- tools/devsecops/pipeline_security_generator.py +475 -0
- tools/devsecops/policy_generator.py +644 -0
- tools/devsecops/profile_manager.py +374 -0
- tools/devsecops/service_mesh_generator.py +1063 -0
- tools/devsecops/zta_maturity_scorer.py +355 -0
- tools/devsecops/zta_terraform_generator.py +1301 -0
- tools/edge_ai/__init__.py +2 -0
- tools/edge_ai/model_manager.py +200 -0
- tools/embedded/__init__.py +2 -0
- tools/embedded/cmake_generator.py +318 -0
- tools/embedded/crash_analyzer.py +191 -0
- tools/embedded/nl_to_firmware.py +277 -0
- tools/events/__init__.py +1 -0
- tools/events/event_bus.py +199 -0
- tools/finetune/pair_generator.py +832 -0
- tools/fleet/__init__.py +2 -0
- tools/fleet/device_registry.py +148 -0
- tools/fleet/ota_manager.py +153 -0
- tools/forge_studio/__init__.py +13 -0
- tools/forge_studio/analytics/__init__.py +0 -0
- tools/forge_studio/analytics/process_miner.py +383 -0
- tools/forge_studio/audit.py +183 -0
- tools/forge_studio/blueprint/__init__.py +2 -0
- tools/forge_studio/blueprint/build_tracker.py +317 -0
- tools/forge_studio/blueprint/export_engine.py +441 -0
- tools/forge_studio/blueprint/parent_client.py +335 -0
- tools/forge_studio/catalog/__init__.py +2 -0
- tools/forge_studio/catalog/component_registry.py +176 -0
- tools/forge_studio/catalog/schema_validator.py +193 -0
- tools/forge_studio/compliance/__init__.py +1 -0
- tools/forge_studio/compliance/compliance_wiring.py +554 -0
- tools/forge_studio/deploy/__init__.py +1 -0
- tools/forge_studio/deploy/airgap_packager.py +466 -0
- tools/forge_studio/deploy/deploy_engine.py +1792 -0
- tools/forge_studio/deploy/env_manager.py +431 -0
- tools/forge_studio/eject/__init__.py +2 -0
- tools/forge_studio/eject/docker_compose_generator.py +237 -0
- tools/forge_studio/eject/eject_engine.py +230 -0
- tools/forge_studio/eject/expo_scaffolder.py +303 -0
- tools/forge_studio/eject/nextjs_scaffolder.py +338 -0
- tools/forge_studio/enterprise/__init__.py +0 -0
- tools/forge_studio/enterprise/custom_frameworks.py +826 -0
- tools/forge_studio/enterprise/hardening_engine.py +1530 -0
- tools/forge_studio/enterprise/sso_manager.py +718 -0
- tools/forge_studio/enterprise/whitelabel_engine.py +887 -0
- tools/forge_studio/formula/__init__.py +0 -0
- tools/forge_studio/formula/expression_engine.py +562 -0
- tools/forge_studio/formula/formula_registry.py +265 -0
- tools/forge_studio/generator/__init__.py +2 -0
- tools/forge_studio/generator/app_generator.py +584 -0
- tools/forge_studio/generator/complexity_detector.py +368 -0
- tools/forge_studio/generator/prompt_templates.py +104 -0
- tools/forge_studio/generator/spec_builder.py +192 -0
- tools/forge_studio/intake_bridge.py +898 -0
- tools/forge_studio/marketplace/__init__.py +0 -0
- tools/forge_studio/marketplace/component_hub.py +428 -0
- tools/forge_studio/models.py +369 -0
- tools/forge_studio/renderer/__init__.py +2 -0
- tools/forge_studio/renderer/json_render_engine.py +623 -0
- tools/forge_studio/renderer/layout_engine.py +214 -0
- tools/forge_studio/renderer/rn_component_map.py +182 -0
- tools/forge_studio/supabase/__init__.py +2 -0
- tools/forge_studio/supabase/auth_generator.py +283 -0
- tools/forge_studio/supabase/migration_generator.py +93 -0
- tools/forge_studio/supabase/schema_generator.py +281 -0
- tools/forge_studio/tenant_manager.py +387 -0
- tools/forge_studio/workflow/__init__.py +2 -0
- tools/forge_studio/workflow/bpmn_adapter.py +489 -0
- tools/govcon/draft_orchestrator.py +1151 -0
- tools/govcon/engine_enrichment.py +373 -0
- tools/govcon/knowledge_base.py +487 -0
- tools/govcon/knowledge_ingestion.py +510 -0
- tools/govcon/sam_scanner.py +754 -0
- tools/harness/__init__.py +6 -0
- tools/harness/exit_criteria_evaluator.py +231 -0
- tools/harness/maturity_assessor.py +347 -0
- tools/harness/scaffold_harness.py +416 -0
- tools/harness/trace_analyzer.py +281 -0
- tools/infra/__init__.py +1 -0
- tools/infra/ansible_generator.py +867 -0
- tools/infra/dockerfile_generator.py +359 -0
- tools/infra/infra_status.py +384 -0
- tools/infra/ironbank_metadata_generator.py +403 -0
- tools/infra/k8s_generator.py +1000 -0
- tools/infra/pipeline_generator.py +830 -0
- tools/infra/rollback.py +389 -0
- tools/infra/terraform_generator.py +1140 -0
- tools/infra/terraform_generator_azure.py +1252 -0
- tools/infra/terraform_generator_gcp.py +951 -0
- tools/infra/terraform_generator_ibm.py +359 -0
- tools/infra/terraform_generator_oci.py +918 -0
- tools/infra/terraform_generator_onprem.py +318 -0
- tools/knowledge/__init__.py +1 -0
- tools/knowledge/knowledge_ingest.py +281 -0
- tools/knowledge/pattern_detector.py +681 -0
- tools/knowledge/recommendation_engine.py +449 -0
- tools/knowledge/self_heal_analyzer.py +492 -0
- tools/knowledge_graph/__init__.py +2 -0
- tools/knowledge_graph/graph_rag.py +498 -0
- tools/knowledge_graph/ingester.py +406 -0
- tools/knowledge_graph/insight_generator.py +369 -0
- tools/knowledge_graph/text_network.py +832 -0
- tools/llm/__init__.py +72 -0
- tools/llm/anthropic_provider.py +170 -0
- tools/llm/azure_openai_provider.py +338 -0
- tools/llm/bedrock_provider.py +315 -0
- tools/llm/embedding_provider.py +438 -0
- tools/llm/gemini_provider.py +381 -0
- tools/llm/ibm_watsonx_provider.py +231 -0
- tools/llm/oci_genai_provider.py +462 -0
- tools/llm/ollama_provider.py +350 -0
- tools/llm/openai_provider.py +225 -0
- tools/llm/prompt_registry.py +447 -0
- tools/llm/provider.py +355 -0
- tools/llm/provider_sdk.py +175 -0
- tools/llm/router.py +1124 -0
- tools/llm/semantic_cache.py +394 -0
- tools/llm/vertex_ai_provider.py +374 -0
- tools/maintenance/__init__.py +2 -0
- tools/maintenance/dependency_scanner.py +1016 -0
- tools/maintenance/maintenance_auditor.py +804 -0
- tools/maintenance/remediation_engine.py +957 -0
- tools/maintenance/vulnerability_checker.py +978 -0
- tools/manifest.md +1066 -0
- tools/marketplace/asset_installer.py +639 -0
- tools/marketplace/feedback_validator.py +359 -0
- tools/marketplace/license_client.py +458 -0
- tools/marketplace/module_crypto.py +544 -0
- tools/marketplace/module_runtime.py +236 -0
- tools/marketplace/token_store.py +264 -0
- tools/mbse/__init__.py +3 -0
- tools/mbse/des_assessor.py +1173 -0
- tools/mbse/des_report_generator.py +787 -0
- tools/mbse/diagram_extractor.py +792 -0
- tools/mbse/digital_thread.py +1650 -0
- tools/mbse/model_code_generator.py +1115 -0
- tools/mbse/model_control_mapper.py +410 -0
- tools/mbse/pi_model_tracker.py +1079 -0
- tools/mbse/reqif_parser.py +1468 -0
- tools/mbse/sync_engine.py +1789 -0
- tools/mbse/thread_heatmap.py +445 -0
- tools/mbse/xmi_parser.py +1558 -0
- tools/mcp/builder_server.py +64 -0
- tools/mcp/compliance_server.py +64 -0
- tools/mcp/connector_forge_server.py +155 -0
- tools/mcp/core_server.py +64 -0
- tools/mcp/devsecops_server.py +11 -0
- tools/mcp/devsecops_zta_server.py +64 -0
- tools/mcp/knowledge_server.py +64 -0
- tools/mcp/monitor_server.py +64 -0
- tools/mcp/ops_server.py +300 -0
- tools/mcp/requirements_analyst_server.py +64 -0
- tools/mcp/requirements_server.py +11 -0
- tools/mcp/security_server.py +64 -0
- tools/mcp/simulation_server.py +64 -0
- tools/mcp/supply_chain_server.py +64 -0
- tools/mcp/tool_registry.py +299 -0
- tools/memory/__init__.py +2 -0
- tools/memory/auto_capture.py +346 -0
- tools/memory/embed_memory.py +157 -0
- tools/memory/history_compressor.py +334 -0
- tools/memory/hybrid_search.py +235 -0
- tools/memory/maintenance_cron.py +288 -0
- tools/memory/memory_consolidation.py +439 -0
- tools/memory/memory_db.py +132 -0
- tools/memory/memory_read.py +101 -0
- tools/memory/memory_write.py +221 -0
- tools/memory/semantic_search.py +138 -0
- tools/memory/time_decay.py +434 -0
- tools/missions/__init__.py +2 -0
- tools/missions/mission_engine.py +459 -0
- tools/monitor/__init__.py +1 -0
- tools/monitor/alert_correlator.py +486 -0
- tools/monitor/auto_resolver.py +603 -0
- tools/monitor/health_checker.py +507 -0
- tools/monitor/heartbeat_daemon.py +779 -0
- tools/monitor/log_analyzer.py +507 -0
- tools/monitor/metric_collector.py +484 -0
- tools/mosa/__init__.py +10 -0
- tools/mosa/icd_generator.py +358 -0
- tools/mosa/modular_design_analyzer.py +682 -0
- tools/mosa/mosa_code_enforcer.py +348 -0
- tools/mosa/tsp_generator.py +265 -0
- tools/observability/__init__.py +100 -0
- tools/observability/genai_attributes.py +88 -0
- tools/observability/instrumentation.py +140 -0
- tools/observability/mlflow_exporter.py +193 -0
- tools/observability/otel_tracer.py +168 -0
- tools/observability/provenance/__init__.py +3 -0
- tools/observability/provenance/prov_recorder.py +322 -0
- tools/observability/shap/__init__.py +3 -0
- tools/observability/shap/agent_shap.py +274 -0
- tools/observability/sqlite_tracer.py +360 -0
- tools/observability/trace_context.py +205 -0
- tools/observability/tracer.py +230 -0
- tools/orchestration/__init__.py +1 -0
- tools/orchestration/peer_channels.py +254 -0
- tools/orchestration/saga_coordinator.py +390 -0
- tools/project/__init__.py +1 -0
- tools/project/manifest_loader.py +418 -0
- tools/project/project_create.py +350 -0
- tools/project/project_list.py +171 -0
- tools/project/project_scaffold.py +1715 -0
- tools/project/project_status.py +478 -0
- tools/project/session_context_builder.py +752 -0
- tools/project/validate_manifest.py +54 -0
- tools/rag/corrective_rag.py +582 -0
- tools/rag/source_registry.py +482 -0
- tools/requirements/__init__.py +1 -0
- tools/requirements/ai_governance_scorer.py +207 -0
- tools/requirements/boundary_analyzer.py +1281 -0
- tools/requirements/clarification_engine.py +605 -0
- tools/requirements/complexity_scorer.py +369 -0
- tools/requirements/consistency_analyzer.py +789 -0
- tools/requirements/constitution_manager.py +592 -0
- tools/requirements/decomposition_engine.py +764 -0
- tools/requirements/document_extractor.py +1002 -0
- tools/requirements/elicitation_techniques.py +508 -0
- tools/requirements/gap_detector.py +260 -0
- tools/requirements/intake_engine.py +2175 -0
- tools/requirements/prd_generator.py +839 -0
- tools/requirements/prd_validator.py +584 -0
- tools/requirements/readiness_scorer.py +302 -0
- tools/requirements/spec_organizer.py +1015 -0
- tools/requirements/spec_quality_checker.py +1083 -0
- tools/requirements/traceability_builder.py +566 -0
- tools/research/__init__.py +3 -0
- tools/research/academic_scanner.py +130 -0
- tools/research/build_buy_analyzer.py +229 -0
- tools/research/challenge_scorer.py +280 -0
- tools/research/community_scanner.py +174 -0
- tools/research/cross_engine_bridge.py +124 -0
- tools/research/dossier_generator.py +305 -0
- tools/research/landscape_scanner.py +315 -0
- tools/research/regulatory_scanner.py +248 -0
- tools/research/research_manager.py +469 -0
- tools/research/source_scanner.py +150 -0
- tools/research/vertical_loader.py +118 -0
- tools/saas/__init__.py +0 -0
- tools/saas/licensing/__init__.py +0 -0
- tools/saas/licensing/license_validator.py +345 -0
- tools/scaffold/__init__.py +2 -0
- tools/scaffold/golden_path.py +504 -0
- tools/security/__init__.py +1 -0
- tools/security/agent_output_validator.py +330 -0
- tools/security/agent_trust_scorer.py +652 -0
- tools/security/ai_bom_generator.py +718 -0
- tools/security/ai_telemetry_logger.py +469 -0
- tools/security/atlas_red_team.py +541 -0
- tools/security/code_pattern_scanner.py +382 -0
- tools/security/confabulation_detector.py +265 -0
- tools/security/container_scanner.py +489 -0
- tools/security/dependency_auditor.py +942 -0
- tools/security/endpoint_security_scanner.py +626 -0
- tools/security/mcp_tool_authorizer.py +242 -0
- tools/security/output_verifier.py +427 -0
- tools/security/prompt_injection_detector.py +737 -0
- tools/security/sast_runner.py +946 -0
- tools/security/secret_detector.py +376 -0
- tools/security/threat_modeler.py +678 -0
- tools/security/tool_chain_validator.py +357 -0
- tools/security/vuln_scanner.py +536 -0
- tools/simulation/__init__.py +2 -0
- tools/simulation/ato_simulator.py +517 -0
- tools/simulation/coa_generator.py +1539 -0
- tools/simulation/monte_carlo.py +745 -0
- tools/simulation/scenario_manager.py +1060 -0
- tools/simulation/simulation_engine.py +1091 -0
- tools/simulator/__init__.py +2 -0
- tools/simulator/sim_runner.py +272 -0
- tools/supply_chain/__init__.py +2 -0
- tools/supply_chain/cve_triager.py +690 -0
- tools/supply_chain/dependency_graph.py +630 -0
- tools/supply_chain/isa_manager.py +526 -0
- tools/supply_chain/scrm_assessor.py +531 -0
- tools/supply_chain/slsa_verifier.py +473 -0
- tools/testing/__init__.py +2 -0
- tools/testing/acceptance_validator.py +411 -0
- tools/testing/api_surface_extractor.py +749 -0
- tools/testing/claude_dir_validator.py +831 -0
- tools/testing/data_types.py +199 -0
- tools/testing/e2e_runner.py +715 -0
- tools/testing/fuzz_cli.py +306 -0
- tools/testing/health_check.py +483 -0
- tools/testing/platform_check.py +143 -0
- tools/testing/production_audit.py +1836 -0
- tools/testing/production_remediate.py +803 -0
- tools/testing/screenshot_validator.py +538 -0
- tools/testing/smoke_test.py +283 -0
- tools/testing/test_agent_models.py +117 -0
- tools/testing/test_orchestrator.py +957 -0
- tools/testing/utils.py +229 -0
- tools/writeguard/__init__.py +1 -0
- tools/writeguard/main.py +1 -0
- tools/writing/__init__.py +7 -0
- tools/writing/ai_content_detector.py +316 -0
- tools/writing/analysis_engine.py +454 -0
- tools/writing/batch_analyzer.py +276 -0
- tools/writing/coherence_analyzer.py +221 -0
- tools/writing/govcon_bridge.py +509 -0
- tools/writing/grammar_checker.py +270 -0
- tools/writing/plagiarism_detector.py +106 -0
- tools/writing/readability_scorer.py +201 -0
- tools/writing/rewriter.py +96 -0
- tools/writing/signal_registrar.py +167 -0
- tools/writing/snippet_manager.py +276 -0
- tools/writing/style_enforcer.py +220 -0
- tools/writing/style_guide_manager.py +438 -0
- tools/writing/tone_profiler.py +168 -0
|
@@ -0,0 +1,1650 @@
|
|
|
1
|
+
# [TEMPLATE: CUI // SP-CTI]
|
|
2
|
+
#!/usr/bin/env python3
|
|
3
|
+
"""ICDEV Digital Thread Engine — end-to-end traceability across the MBSE lifecycle.
|
|
4
|
+
|
|
5
|
+
Manages N:M links between: DOORS requirements -> SysML elements -> code modules
|
|
6
|
+
-> test files -> NIST controls -> STIG rules -> compliance artifacts.
|
|
7
|
+
|
|
8
|
+
Supports forward/backward trace, coverage analysis, orphan/gap detection,
|
|
9
|
+
heuristic auto-linking, and CUI-marked traceability reports.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import re
|
|
15
|
+
import sqlite3
|
|
16
|
+
import sys
|
|
17
|
+
from collections import deque
|
|
18
|
+
from datetime import datetime
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from tools.db.storage import get_connection
|
|
21
|
+
DB_PATH = None # Storage layer handles path resolution (D-DB-20)
|
|
22
|
+
|
|
23
|
+
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
|
24
|
+
|
|
25
|
+
# Audit trail integration (graceful fallback for standalone use)
|
|
26
|
+
try:
|
|
27
|
+
sys.path.insert(0, str(BASE_DIR))
|
|
28
|
+
from tools.audit.audit_logger import log_event
|
|
29
|
+
except ImportError:
|
|
30
|
+
def log_event(**kwargs):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
# Valid artifact types in the digital thread
|
|
34
|
+
VALID_TYPES = (
|
|
35
|
+
"doors_requirement", "sysml_element", "code_module",
|
|
36
|
+
"test_file", "nist_control", "stig_rule", "compliance_artifact",
|
|
37
|
+
"interface_spec", # Phase 26: MOSA interface specifications
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Valid link relationship types
|
|
41
|
+
VALID_LINK_TYPES = (
|
|
42
|
+
"satisfies", "derives_from", "implements", "verifies",
|
|
43
|
+
"traces_to", "allocates", "refines", "maps_to",
|
|
44
|
+
"defines_interface", # Phase 26: MOSA interface definition link
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Expected chain order for completeness analysis
|
|
48
|
+
THREAD_CHAIN = [
|
|
49
|
+
"doors_requirement", "sysml_element", "code_module",
|
|
50
|
+
"test_file", "nist_control",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# Keyword-to-NIST-control-family mapping for auto-linking
|
|
54
|
+
CONTROL_KEYWORD_MAP = {
|
|
55
|
+
"AC": ["auth", "access", "login", "permission", "role", "rbac", "authorization"],
|
|
56
|
+
"AU": ["audit", "log", "logging", "trail", "monitor", "event"],
|
|
57
|
+
"SC": ["encrypt", "crypto", "tls", "ssl", "certificate", "cipher", "hash"],
|
|
58
|
+
"IA": ["identity", "authenticate", "credential", "password", "mfa", "token"],
|
|
59
|
+
"CM": ["config", "configuration", "baseline", "change_management"],
|
|
60
|
+
"SI": ["integrity", "validation", "sanitize", "input_check", "patch"],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# Helper: resolve human-readable name for an element
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
def _resolve_element_name(element_type: str, element_id: str, conn) -> str:
|
|
68
|
+
"""Resolve human-readable name for an element by type.
|
|
69
|
+
|
|
70
|
+
doors_requirement -> doors_requirements.title
|
|
71
|
+
sysml_element -> sysml_elements.name
|
|
72
|
+
code_module -> just the path string
|
|
73
|
+
test_file -> just the path string
|
|
74
|
+
nist_control -> compliance_controls.title
|
|
75
|
+
stig_rule -> stig_findings.title
|
|
76
|
+
compliance_artifact -> 'artifact: ' + element_id
|
|
77
|
+
"""
|
|
78
|
+
c = conn.cursor()
|
|
79
|
+
try:
|
|
80
|
+
if element_type == "doors_requirement":
|
|
81
|
+
c.execute("SELECT title FROM doors_requirements WHERE id = ?", (element_id,))
|
|
82
|
+
row = c.fetchone()
|
|
83
|
+
return row[0] if row else element_id
|
|
84
|
+
elif element_type == "sysml_element":
|
|
85
|
+
c.execute("SELECT name FROM sysml_elements WHERE id = ?", (element_id,))
|
|
86
|
+
row = c.fetchone()
|
|
87
|
+
return row[0] if row else element_id
|
|
88
|
+
elif element_type == "code_module":
|
|
89
|
+
return element_id
|
|
90
|
+
elif element_type == "test_file":
|
|
91
|
+
return element_id
|
|
92
|
+
elif element_type == "nist_control":
|
|
93
|
+
c.execute("SELECT title FROM compliance_controls WHERE id = ?", (element_id,))
|
|
94
|
+
row = c.fetchone()
|
|
95
|
+
return row[0] if row else element_id
|
|
96
|
+
elif element_type == "stig_rule":
|
|
97
|
+
c.execute("SELECT title FROM stig_findings WHERE rule_id = ?", (element_id,))
|
|
98
|
+
row = c.fetchone()
|
|
99
|
+
return row[0] if row else element_id
|
|
100
|
+
elif element_type == "compliance_artifact":
|
|
101
|
+
return f"artifact: {element_id}"
|
|
102
|
+
else:
|
|
103
|
+
return element_id
|
|
104
|
+
except Exception:
|
|
105
|
+
return element_id
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
# Core: create_link
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
def create_link(project_id: str, source_type: str, source_id: str,
|
|
112
|
+
target_type: str, target_id: str, link_type: str,
|
|
113
|
+
evidence: str = None, confidence: float = 1.0,
|
|
114
|
+
created_by: str = "icdev-mbse-engine", db_path=None) -> dict:
|
|
115
|
+
"""Create a digital thread link. Returns {"id": int, "created": bool} or error.
|
|
116
|
+
|
|
117
|
+
Uses INSERT OR REPLACE for idempotency.
|
|
118
|
+
"""
|
|
119
|
+
if source_type not in VALID_TYPES:
|
|
120
|
+
return {"error": f"Invalid source_type '{source_type}'. Valid: {VALID_TYPES}"}
|
|
121
|
+
if target_type not in VALID_TYPES:
|
|
122
|
+
return {"error": f"Invalid target_type '{target_type}'. Valid: {VALID_TYPES}"}
|
|
123
|
+
if link_type not in VALID_LINK_TYPES:
|
|
124
|
+
return {"error": f"Invalid link_type '{link_type}'. Valid: {VALID_LINK_TYPES}"}
|
|
125
|
+
if not (0.0 <= confidence <= 1.0):
|
|
126
|
+
return {"error": "Confidence must be between 0.0 and 1.0"}
|
|
127
|
+
|
|
128
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
129
|
+
c = conn.cursor()
|
|
130
|
+
try:
|
|
131
|
+
c.execute(
|
|
132
|
+
"""INSERT OR REPLACE INTO digital_thread_links
|
|
133
|
+
(project_id, source_type, source_id, target_type, target_id,
|
|
134
|
+
link_type, confidence, evidence, created_by, created_at)
|
|
135
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
136
|
+
(project_id, source_type, source_id, target_type, target_id,
|
|
137
|
+
link_type, confidence, evidence, created_by,
|
|
138
|
+
datetime.now().isoformat()),
|
|
139
|
+
)
|
|
140
|
+
conn.commit()
|
|
141
|
+
link_id = c.lastrowid
|
|
142
|
+
created = True
|
|
143
|
+
|
|
144
|
+
# Audit trail
|
|
145
|
+
try:
|
|
146
|
+
log_event(
|
|
147
|
+
event_type="digital_thread_linked",
|
|
148
|
+
actor=created_by,
|
|
149
|
+
action=f"Linked {source_type}:{source_id} -> {target_type}:{target_id} ({link_type})",
|
|
150
|
+
project_id=project_id,
|
|
151
|
+
details={
|
|
152
|
+
"link_id": link_id,
|
|
153
|
+
"source_type": source_type,
|
|
154
|
+
"source_id": source_id,
|
|
155
|
+
"target_type": target_type,
|
|
156
|
+
"target_id": target_id,
|
|
157
|
+
"link_type": link_type,
|
|
158
|
+
"confidence": confidence,
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
except Exception:
|
|
162
|
+
pass # Audit failure should not block link creation
|
|
163
|
+
|
|
164
|
+
return {"id": link_id, "created": created}
|
|
165
|
+
except Exception as e:
|
|
166
|
+
return {"error": str(e)}
|
|
167
|
+
finally:
|
|
168
|
+
conn.close()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
# Core: delete_link
|
|
173
|
+
# ---------------------------------------------------------------------------
|
|
174
|
+
def delete_link(project_id: str, link_id: int, db_path=None) -> bool:
|
|
175
|
+
"""Delete a specific link by ID. Returns True if deleted, False otherwise."""
|
|
176
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
177
|
+
c = conn.cursor()
|
|
178
|
+
try:
|
|
179
|
+
c.execute(
|
|
180
|
+
"DELETE FROM digital_thread_links WHERE id = ? AND project_id = ?",
|
|
181
|
+
(link_id, project_id),
|
|
182
|
+
)
|
|
183
|
+
conn.commit()
|
|
184
|
+
deleted = c.rowcount > 0
|
|
185
|
+
return deleted
|
|
186
|
+
except Exception:
|
|
187
|
+
return False
|
|
188
|
+
finally:
|
|
189
|
+
conn.close()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# ---------------------------------------------------------------------------
|
|
193
|
+
# Trace: forward (BFS)
|
|
194
|
+
# ---------------------------------------------------------------------------
|
|
195
|
+
def get_forward_trace(project_id: str, source_type: str, source_id: str,
|
|
196
|
+
max_depth: int = 10, db_path=None) -> dict:
|
|
197
|
+
"""Trace forward from a source element through the digital thread.
|
|
198
|
+
|
|
199
|
+
Uses BFS traversal. Returns tree structure:
|
|
200
|
+
{"source": {"type": str, "id": str, "name": str}, "links": [
|
|
201
|
+
{"link_type": str, "target": {"type": str, "id": str, "name": str},
|
|
202
|
+
"confidence": float, "children": [...]}
|
|
203
|
+
]}
|
|
204
|
+
"""
|
|
205
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
206
|
+
|
|
207
|
+
source_name = _resolve_element_name(source_type, source_id, conn)
|
|
208
|
+
result = {
|
|
209
|
+
"source": {"type": source_type, "id": source_id, "name": source_name},
|
|
210
|
+
"links": [],
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# BFS traversal
|
|
214
|
+
visited = set()
|
|
215
|
+
visited.add((source_type, source_id))
|
|
216
|
+
queue = deque()
|
|
217
|
+
queue.append((source_type, source_id, result["links"], 0))
|
|
218
|
+
|
|
219
|
+
c = conn.cursor()
|
|
220
|
+
while queue:
|
|
221
|
+
curr_type, curr_id, parent_links, depth = queue.popleft()
|
|
222
|
+
if depth >= max_depth:
|
|
223
|
+
continue
|
|
224
|
+
|
|
225
|
+
c.execute(
|
|
226
|
+
"""SELECT target_type, target_id, link_type, confidence, evidence
|
|
227
|
+
FROM digital_thread_links
|
|
228
|
+
WHERE project_id = ? AND source_type = ? AND source_id = ?""",
|
|
229
|
+
(project_id, curr_type, curr_id),
|
|
230
|
+
)
|
|
231
|
+
rows = c.fetchall()
|
|
232
|
+
for row in rows:
|
|
233
|
+
t_type = row["target_type"]
|
|
234
|
+
t_id = row["target_id"]
|
|
235
|
+
key = (t_type, t_id)
|
|
236
|
+
|
|
237
|
+
t_name = _resolve_element_name(t_type, t_id, conn)
|
|
238
|
+
child_links = []
|
|
239
|
+
node = {
|
|
240
|
+
"link_type": row["link_type"],
|
|
241
|
+
"confidence": row["confidence"],
|
|
242
|
+
"target": {"type": t_type, "id": t_id, "name": t_name},
|
|
243
|
+
"children": child_links,
|
|
244
|
+
}
|
|
245
|
+
parent_links.append(node)
|
|
246
|
+
|
|
247
|
+
if key not in visited:
|
|
248
|
+
visited.add(key)
|
|
249
|
+
queue.append((t_type, t_id, child_links, depth + 1))
|
|
250
|
+
|
|
251
|
+
conn.close()
|
|
252
|
+
return result
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# ---------------------------------------------------------------------------
|
|
256
|
+
# Trace: backward (BFS)
|
|
257
|
+
# ---------------------------------------------------------------------------
|
|
258
|
+
def get_backward_trace(project_id: str, target_type: str, target_id: str,
|
|
259
|
+
max_depth: int = 10, db_path=None) -> dict:
|
|
260
|
+
"""Trace backward from a target element. Same tree structure but reversed."""
|
|
261
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
262
|
+
|
|
263
|
+
target_name = _resolve_element_name(target_type, target_id, conn)
|
|
264
|
+
result = {
|
|
265
|
+
"target": {"type": target_type, "id": target_id, "name": target_name},
|
|
266
|
+
"links": [],
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
# BFS traversal backward
|
|
270
|
+
visited = set()
|
|
271
|
+
visited.add((target_type, target_id))
|
|
272
|
+
queue = deque()
|
|
273
|
+
queue.append((target_type, target_id, result["links"], 0))
|
|
274
|
+
|
|
275
|
+
c = conn.cursor()
|
|
276
|
+
while queue:
|
|
277
|
+
curr_type, curr_id, parent_links, depth = queue.popleft()
|
|
278
|
+
if depth >= max_depth:
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
c.execute(
|
|
282
|
+
"""SELECT source_type, source_id, link_type, confidence, evidence
|
|
283
|
+
FROM digital_thread_links
|
|
284
|
+
WHERE project_id = ? AND target_type = ? AND target_id = ?""",
|
|
285
|
+
(project_id, curr_type, curr_id),
|
|
286
|
+
)
|
|
287
|
+
rows = c.fetchall()
|
|
288
|
+
for row in rows:
|
|
289
|
+
s_type = row["source_type"]
|
|
290
|
+
s_id = row["source_id"]
|
|
291
|
+
key = (s_type, s_id)
|
|
292
|
+
|
|
293
|
+
s_name = _resolve_element_name(s_type, s_id, conn)
|
|
294
|
+
child_links = []
|
|
295
|
+
node = {
|
|
296
|
+
"link_type": row["link_type"],
|
|
297
|
+
"confidence": row["confidence"],
|
|
298
|
+
"source": {"type": s_type, "id": s_id, "name": s_name},
|
|
299
|
+
"children": child_links,
|
|
300
|
+
}
|
|
301
|
+
parent_links.append(node)
|
|
302
|
+
|
|
303
|
+
if key not in visited:
|
|
304
|
+
visited.add(key)
|
|
305
|
+
queue.append((s_type, s_id, child_links, depth + 1))
|
|
306
|
+
|
|
307
|
+
conn.close()
|
|
308
|
+
return result
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
# ---------------------------------------------------------------------------
|
|
312
|
+
# Trace: full bidirectional
|
|
313
|
+
# ---------------------------------------------------------------------------
|
|
314
|
+
def get_full_thread(project_id: str, element_type: str, element_id: str,
|
|
315
|
+
db_path=None) -> dict:
|
|
316
|
+
"""Complete bidirectional trace from any point. Returns both forward and backward."""
|
|
317
|
+
forward = get_forward_trace(project_id, element_type, element_id, db_path=db_path)
|
|
318
|
+
backward = get_backward_trace(project_id, element_type, element_id, db_path=db_path)
|
|
319
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
320
|
+
name = _resolve_element_name(element_type, element_id, conn)
|
|
321
|
+
conn.close()
|
|
322
|
+
return {
|
|
323
|
+
"element": {"type": element_type, "id": element_id, "name": name},
|
|
324
|
+
"forward": forward.get("links", []),
|
|
325
|
+
"backward": backward.get("links", []),
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# ---------------------------------------------------------------------------
|
|
330
|
+
# Coverage analysis
|
|
331
|
+
# ---------------------------------------------------------------------------
|
|
332
|
+
def compute_coverage(project_id: str, db_path=None) -> dict:
|
|
333
|
+
"""Coverage metrics across the digital thread.
|
|
334
|
+
|
|
335
|
+
- requirement_coverage: % of doors_requirements linked to sysml_elements
|
|
336
|
+
- model_coverage: % of sysml_elements (blocks only) linked to code_modules
|
|
337
|
+
- test_coverage: % of code_modules linked to test_files
|
|
338
|
+
- control_coverage: % of project_controls linked to any thread element
|
|
339
|
+
- overall_thread_completeness: % of requirements with full chain
|
|
340
|
+
(req -> model -> code -> test -> control)
|
|
341
|
+
"""
|
|
342
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
343
|
+
c = conn.cursor()
|
|
344
|
+
|
|
345
|
+
# Total DOORS requirements for this project
|
|
346
|
+
c.execute("SELECT COUNT(*) FROM doors_requirements WHERE project_id = ?", (project_id,))
|
|
347
|
+
total_reqs = c.fetchone()[0]
|
|
348
|
+
|
|
349
|
+
# Requirements linked to sysml_elements
|
|
350
|
+
c.execute(
|
|
351
|
+
"""SELECT COUNT(DISTINCT source_id) FROM digital_thread_links
|
|
352
|
+
WHERE project_id = ? AND source_type = 'doors_requirement'
|
|
353
|
+
AND target_type = 'sysml_element'""",
|
|
354
|
+
(project_id,),
|
|
355
|
+
)
|
|
356
|
+
linked_reqs = c.fetchone()[0]
|
|
357
|
+
|
|
358
|
+
# Total SysML blocks for this project
|
|
359
|
+
c.execute(
|
|
360
|
+
"SELECT COUNT(*) FROM sysml_elements WHERE project_id = ? AND element_type = 'block'",
|
|
361
|
+
(project_id,),
|
|
362
|
+
)
|
|
363
|
+
total_blocks = c.fetchone()[0]
|
|
364
|
+
|
|
365
|
+
# Blocks linked to code_modules
|
|
366
|
+
c.execute(
|
|
367
|
+
"""SELECT COUNT(DISTINCT source_id) FROM digital_thread_links
|
|
368
|
+
WHERE project_id = ? AND source_type = 'sysml_element'
|
|
369
|
+
AND target_type = 'code_module'""",
|
|
370
|
+
(project_id,),
|
|
371
|
+
)
|
|
372
|
+
linked_blocks = c.fetchone()[0]
|
|
373
|
+
|
|
374
|
+
# Unique code_modules that are link sources or targets
|
|
375
|
+
c.execute(
|
|
376
|
+
"""SELECT COUNT(DISTINCT cm) FROM (
|
|
377
|
+
SELECT source_id AS cm FROM digital_thread_links
|
|
378
|
+
WHERE project_id = ? AND source_type = 'code_module'
|
|
379
|
+
UNION
|
|
380
|
+
SELECT target_id AS cm FROM digital_thread_links
|
|
381
|
+
WHERE project_id = ? AND target_type = 'code_module'
|
|
382
|
+
) AS subq""",
|
|
383
|
+
(project_id, project_id),
|
|
384
|
+
)
|
|
385
|
+
total_code = c.fetchone()[0]
|
|
386
|
+
|
|
387
|
+
# Code modules linked to test_files
|
|
388
|
+
c.execute(
|
|
389
|
+
"""SELECT COUNT(DISTINCT source_id) FROM digital_thread_links
|
|
390
|
+
WHERE project_id = ? AND source_type = 'code_module'
|
|
391
|
+
AND target_type = 'test_file'""",
|
|
392
|
+
(project_id,),
|
|
393
|
+
)
|
|
394
|
+
code_with_tests = c.fetchone()[0]
|
|
395
|
+
|
|
396
|
+
# Total project_controls
|
|
397
|
+
c.execute("SELECT COUNT(*) FROM project_controls WHERE project_id = ?", (project_id,))
|
|
398
|
+
total_controls = c.fetchone()[0]
|
|
399
|
+
|
|
400
|
+
# Controls linked to any thread element
|
|
401
|
+
c.execute(
|
|
402
|
+
"""SELECT COUNT(DISTINCT pc.control_id) FROM project_controls pc
|
|
403
|
+
WHERE pc.project_id = ?
|
|
404
|
+
AND (EXISTS (
|
|
405
|
+
SELECT 1 FROM digital_thread_links dtl
|
|
406
|
+
WHERE dtl.project_id = pc.project_id
|
|
407
|
+
AND ((dtl.source_type = 'nist_control' AND dtl.source_id = pc.control_id)
|
|
408
|
+
OR (dtl.target_type = 'nist_control' AND dtl.target_id = pc.control_id))
|
|
409
|
+
))""",
|
|
410
|
+
(project_id,),
|
|
411
|
+
)
|
|
412
|
+
linked_controls = c.fetchone()[0]
|
|
413
|
+
|
|
414
|
+
# Full chain completeness: requirement -> model -> code -> test -> control
|
|
415
|
+
# For each requirement, check if a full chain exists
|
|
416
|
+
c.execute("SELECT id FROM doors_requirements WHERE project_id = ?", (project_id,))
|
|
417
|
+
req_ids = [row[0] for row in c.fetchall()]
|
|
418
|
+
full_chain_count = 0
|
|
419
|
+
|
|
420
|
+
for req_id in req_ids:
|
|
421
|
+
# req -> sysml_element
|
|
422
|
+
c.execute(
|
|
423
|
+
"""SELECT target_id FROM digital_thread_links
|
|
424
|
+
WHERE project_id = ? AND source_type = 'doors_requirement'
|
|
425
|
+
AND source_id = ? AND target_type = 'sysml_element'""",
|
|
426
|
+
(project_id, req_id),
|
|
427
|
+
)
|
|
428
|
+
model_ids = [row[0] for row in c.fetchall()]
|
|
429
|
+
if not model_ids:
|
|
430
|
+
continue
|
|
431
|
+
|
|
432
|
+
has_full_chain = False
|
|
433
|
+
for model_id in model_ids:
|
|
434
|
+
# sysml_element -> code_module
|
|
435
|
+
c.execute(
|
|
436
|
+
"""SELECT target_id FROM digital_thread_links
|
|
437
|
+
WHERE project_id = ? AND source_type = 'sysml_element'
|
|
438
|
+
AND source_id = ? AND target_type = 'code_module'""",
|
|
439
|
+
(project_id, model_id),
|
|
440
|
+
)
|
|
441
|
+
code_ids = [row[0] for row in c.fetchall()]
|
|
442
|
+
if not code_ids:
|
|
443
|
+
continue
|
|
444
|
+
|
|
445
|
+
for code_id in code_ids:
|
|
446
|
+
# code_module -> test_file
|
|
447
|
+
c.execute(
|
|
448
|
+
"""SELECT target_id FROM digital_thread_links
|
|
449
|
+
WHERE project_id = ? AND source_type = 'code_module'
|
|
450
|
+
AND source_id = ? AND target_type = 'test_file'""",
|
|
451
|
+
(project_id, code_id),
|
|
452
|
+
)
|
|
453
|
+
test_ids = [row[0] for row in c.fetchall()]
|
|
454
|
+
if not test_ids:
|
|
455
|
+
continue
|
|
456
|
+
|
|
457
|
+
# Any element in chain -> nist_control
|
|
458
|
+
c.execute(
|
|
459
|
+
"""SELECT 1 FROM digital_thread_links
|
|
460
|
+
WHERE project_id = ?
|
|
461
|
+
AND target_type = 'nist_control'
|
|
462
|
+
AND (
|
|
463
|
+
(source_type = 'doors_requirement' AND source_id = ?)
|
|
464
|
+
OR (source_type = 'sysml_element' AND source_id = ?)
|
|
465
|
+
OR (source_type = 'code_module' AND source_id = ?)
|
|
466
|
+
OR (source_type = 'test_file' AND source_id IN ({}))
|
|
467
|
+
)
|
|
468
|
+
LIMIT 1""".format(",".join("?" * len(test_ids))),
|
|
469
|
+
(project_id, req_id, model_id, code_id, *test_ids),
|
|
470
|
+
)
|
|
471
|
+
if c.fetchone():
|
|
472
|
+
has_full_chain = True
|
|
473
|
+
break
|
|
474
|
+
break # Only need one path
|
|
475
|
+
if has_full_chain:
|
|
476
|
+
break
|
|
477
|
+
|
|
478
|
+
if has_full_chain:
|
|
479
|
+
full_chain_count += 1
|
|
480
|
+
|
|
481
|
+
def pct(num, denom):
|
|
482
|
+
return round((num / denom) * 100, 2) if denom > 0 else 0.0
|
|
483
|
+
|
|
484
|
+
conn.close()
|
|
485
|
+
return {
|
|
486
|
+
"requirement_coverage": pct(linked_reqs, total_reqs),
|
|
487
|
+
"model_coverage": pct(linked_blocks, total_blocks),
|
|
488
|
+
"test_coverage": pct(code_with_tests, total_code),
|
|
489
|
+
"control_coverage": pct(linked_controls, total_controls),
|
|
490
|
+
"overall_thread_completeness": pct(full_chain_count, total_reqs),
|
|
491
|
+
"details": {
|
|
492
|
+
"total_requirements": total_reqs,
|
|
493
|
+
"requirements_linked": linked_reqs,
|
|
494
|
+
"total_blocks": total_blocks,
|
|
495
|
+
"blocks_linked": linked_blocks,
|
|
496
|
+
"total_code_modules": total_code,
|
|
497
|
+
"code_with_tests": code_with_tests,
|
|
498
|
+
"total_controls": total_controls,
|
|
499
|
+
"controls_linked": linked_controls,
|
|
500
|
+
"full_chain_requirements": full_chain_count,
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
# ---------------------------------------------------------------------------
|
|
506
|
+
# Orphan detection
|
|
507
|
+
# ---------------------------------------------------------------------------
|
|
508
|
+
def find_orphans(project_id: str, db_path=None) -> dict:
|
|
509
|
+
"""Find elements with no links in either direction.
|
|
510
|
+
|
|
511
|
+
- requirements_without_model: DOORS requirements not linked to any sysml_element
|
|
512
|
+
- blocks_without_code: SysML blocks not linked to any code_module
|
|
513
|
+
- code_without_tests: code_modules not linked to any test_file
|
|
514
|
+
- controls_without_evidence: NIST controls not linked to any thread element
|
|
515
|
+
"""
|
|
516
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
517
|
+
c = conn.cursor()
|
|
518
|
+
|
|
519
|
+
# Requirements without model links
|
|
520
|
+
c.execute(
|
|
521
|
+
"""SELECT dr.id, dr.doors_id, dr.title FROM doors_requirements dr
|
|
522
|
+
WHERE dr.project_id = ?
|
|
523
|
+
AND dr.id NOT IN (
|
|
524
|
+
SELECT source_id FROM digital_thread_links
|
|
525
|
+
WHERE project_id = ? AND source_type = 'doors_requirement'
|
|
526
|
+
AND target_type = 'sysml_element'
|
|
527
|
+
)
|
|
528
|
+
AND dr.id NOT IN (
|
|
529
|
+
SELECT target_id FROM digital_thread_links
|
|
530
|
+
WHERE project_id = ? AND target_type = 'doors_requirement'
|
|
531
|
+
AND source_type = 'sysml_element'
|
|
532
|
+
)""",
|
|
533
|
+
(project_id, project_id, project_id),
|
|
534
|
+
)
|
|
535
|
+
reqs_orphans = [{"id": r[0], "doors_id": r[1], "title": r[2]} for r in c.fetchall()]
|
|
536
|
+
|
|
537
|
+
# SysML blocks without code links
|
|
538
|
+
c.execute(
|
|
539
|
+
"""SELECT se.id, se.name FROM sysml_elements se
|
|
540
|
+
WHERE se.project_id = ? AND se.element_type = 'block'
|
|
541
|
+
AND se.id NOT IN (
|
|
542
|
+
SELECT source_id FROM digital_thread_links
|
|
543
|
+
WHERE project_id = ? AND source_type = 'sysml_element'
|
|
544
|
+
AND target_type = 'code_module'
|
|
545
|
+
)
|
|
546
|
+
AND se.id NOT IN (
|
|
547
|
+
SELECT target_id FROM digital_thread_links
|
|
548
|
+
WHERE project_id = ? AND target_type = 'sysml_element'
|
|
549
|
+
AND source_type = 'code_module'
|
|
550
|
+
)""",
|
|
551
|
+
(project_id, project_id, project_id),
|
|
552
|
+
)
|
|
553
|
+
blocks_orphans = [{"id": r[0], "name": r[1]} for r in c.fetchall()]
|
|
554
|
+
|
|
555
|
+
# Code modules without test links
|
|
556
|
+
# Gather all code_modules that appear in links for this project
|
|
557
|
+
c.execute(
|
|
558
|
+
"""SELECT DISTINCT cm FROM (
|
|
559
|
+
SELECT source_id AS cm FROM digital_thread_links
|
|
560
|
+
WHERE project_id = ? AND source_type = 'code_module'
|
|
561
|
+
UNION
|
|
562
|
+
SELECT target_id AS cm FROM digital_thread_links
|
|
563
|
+
WHERE project_id = ? AND target_type = 'code_module'
|
|
564
|
+
) AS subq""",
|
|
565
|
+
(project_id, project_id),
|
|
566
|
+
)
|
|
567
|
+
all_code = [row[0] for row in c.fetchall()]
|
|
568
|
+
|
|
569
|
+
code_orphans = []
|
|
570
|
+
for code_id in all_code:
|
|
571
|
+
c.execute(
|
|
572
|
+
"""SELECT 1 FROM digital_thread_links
|
|
573
|
+
WHERE project_id = ?
|
|
574
|
+
AND ((source_type = 'code_module' AND source_id = ? AND target_type = 'test_file')
|
|
575
|
+
OR (target_type = 'code_module' AND target_id = ? AND source_type = 'test_file'))
|
|
576
|
+
LIMIT 1""",
|
|
577
|
+
(project_id, code_id, code_id),
|
|
578
|
+
)
|
|
579
|
+
if not c.fetchone():
|
|
580
|
+
code_orphans.append({"id": code_id})
|
|
581
|
+
|
|
582
|
+
# NIST controls without evidence (no links at all)
|
|
583
|
+
c.execute(
|
|
584
|
+
"""SELECT pc.control_id FROM project_controls pc
|
|
585
|
+
WHERE pc.project_id = ?
|
|
586
|
+
AND NOT EXISTS (
|
|
587
|
+
SELECT 1 FROM digital_thread_links dtl
|
|
588
|
+
WHERE dtl.project_id = pc.project_id
|
|
589
|
+
AND ((dtl.source_type = 'nist_control' AND dtl.source_id = pc.control_id)
|
|
590
|
+
OR (dtl.target_type = 'nist_control' AND dtl.target_id = pc.control_id))
|
|
591
|
+
)""",
|
|
592
|
+
(project_id,),
|
|
593
|
+
)
|
|
594
|
+
control_orphans = [{"control_id": r[0]} for r in c.fetchall()]
|
|
595
|
+
|
|
596
|
+
conn.close()
|
|
597
|
+
return {
|
|
598
|
+
"requirements_without_model": {
|
|
599
|
+
"count": len(reqs_orphans),
|
|
600
|
+
"items": reqs_orphans,
|
|
601
|
+
},
|
|
602
|
+
"blocks_without_code": {
|
|
603
|
+
"count": len(blocks_orphans),
|
|
604
|
+
"items": blocks_orphans,
|
|
605
|
+
},
|
|
606
|
+
"code_without_tests": {
|
|
607
|
+
"count": len(code_orphans),
|
|
608
|
+
"items": code_orphans,
|
|
609
|
+
},
|
|
610
|
+
"controls_without_evidence": {
|
|
611
|
+
"count": len(control_orphans),
|
|
612
|
+
"items": control_orphans,
|
|
613
|
+
},
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
# ---------------------------------------------------------------------------
|
|
618
|
+
# Gap detection
|
|
619
|
+
# ---------------------------------------------------------------------------
|
|
620
|
+
def find_gaps(project_id: str, db_path=None) -> dict:
|
|
621
|
+
"""Find missing links in expected chains.
|
|
622
|
+
|
|
623
|
+
- requirement has model link but model has no code link
|
|
624
|
+
- model has code link but code has no test link
|
|
625
|
+
- code has test link but no control link
|
|
626
|
+
"""
|
|
627
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
628
|
+
c = conn.cursor()
|
|
629
|
+
gaps = []
|
|
630
|
+
|
|
631
|
+
# Gap 1: requirement -> model exists, but model -> code missing
|
|
632
|
+
c.execute(
|
|
633
|
+
"""SELECT dtl.source_id, dtl.target_id
|
|
634
|
+
FROM digital_thread_links dtl
|
|
635
|
+
WHERE dtl.project_id = ?
|
|
636
|
+
AND dtl.source_type = 'doors_requirement'
|
|
637
|
+
AND dtl.target_type = 'sysml_element'""",
|
|
638
|
+
(project_id,),
|
|
639
|
+
)
|
|
640
|
+
req_model_links = c.fetchall()
|
|
641
|
+
for req_id, model_id in req_model_links:
|
|
642
|
+
c.execute(
|
|
643
|
+
"""SELECT 1 FROM digital_thread_links
|
|
644
|
+
WHERE project_id = ? AND source_type = 'sysml_element'
|
|
645
|
+
AND source_id = ? AND target_type = 'code_module'
|
|
646
|
+
LIMIT 1""",
|
|
647
|
+
(project_id, model_id),
|
|
648
|
+
)
|
|
649
|
+
if not c.fetchone():
|
|
650
|
+
req_name = _resolve_element_name("doors_requirement", req_id, conn)
|
|
651
|
+
model_name = _resolve_element_name("sysml_element", model_id, conn)
|
|
652
|
+
gaps.append({
|
|
653
|
+
"gap_type": "model_without_code",
|
|
654
|
+
"description": (
|
|
655
|
+
f"Requirement '{req_name}' ({req_id}) traces to model "
|
|
656
|
+
f"'{model_name}' ({model_id}), but model has no code link"
|
|
657
|
+
),
|
|
658
|
+
"requirement_id": req_id,
|
|
659
|
+
"model_id": model_id,
|
|
660
|
+
"missing_link": "sysml_element -> code_module",
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
# Gap 2: model -> code exists, but code -> test missing
|
|
664
|
+
c.execute(
|
|
665
|
+
"""SELECT dtl.source_id, dtl.target_id
|
|
666
|
+
FROM digital_thread_links dtl
|
|
667
|
+
WHERE dtl.project_id = ?
|
|
668
|
+
AND dtl.source_type = 'sysml_element'
|
|
669
|
+
AND dtl.target_type = 'code_module'""",
|
|
670
|
+
(project_id,),
|
|
671
|
+
)
|
|
672
|
+
model_code_links = c.fetchall()
|
|
673
|
+
for model_id, code_id in model_code_links:
|
|
674
|
+
c.execute(
|
|
675
|
+
"""SELECT 1 FROM digital_thread_links
|
|
676
|
+
WHERE project_id = ? AND source_type = 'code_module'
|
|
677
|
+
AND source_id = ? AND target_type = 'test_file'
|
|
678
|
+
LIMIT 1""",
|
|
679
|
+
(project_id, code_id),
|
|
680
|
+
)
|
|
681
|
+
if not c.fetchone():
|
|
682
|
+
model_name = _resolve_element_name("sysml_element", model_id, conn)
|
|
683
|
+
gaps.append({
|
|
684
|
+
"gap_type": "code_without_test",
|
|
685
|
+
"description": (
|
|
686
|
+
f"Model '{model_name}' ({model_id}) traces to code "
|
|
687
|
+
f"'{code_id}', but code has no test link"
|
|
688
|
+
),
|
|
689
|
+
"model_id": model_id,
|
|
690
|
+
"code_id": code_id,
|
|
691
|
+
"missing_link": "code_module -> test_file",
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
# Gap 3: code -> test exists, but no control link from any chain element
|
|
695
|
+
c.execute(
|
|
696
|
+
"""SELECT dtl.source_id, dtl.target_id
|
|
697
|
+
FROM digital_thread_links dtl
|
|
698
|
+
WHERE dtl.project_id = ?
|
|
699
|
+
AND dtl.source_type = 'code_module'
|
|
700
|
+
AND dtl.target_type = 'test_file'""",
|
|
701
|
+
(project_id,),
|
|
702
|
+
)
|
|
703
|
+
code_test_links = c.fetchall()
|
|
704
|
+
for code_id, test_id in code_test_links:
|
|
705
|
+
c.execute(
|
|
706
|
+
"""SELECT 1 FROM digital_thread_links
|
|
707
|
+
WHERE project_id = ?
|
|
708
|
+
AND target_type = 'nist_control'
|
|
709
|
+
AND (
|
|
710
|
+
(source_type = 'code_module' AND source_id = ?)
|
|
711
|
+
OR (source_type = 'test_file' AND source_id = ?)
|
|
712
|
+
)
|
|
713
|
+
LIMIT 1""",
|
|
714
|
+
(project_id, code_id, test_id),
|
|
715
|
+
)
|
|
716
|
+
if not c.fetchone():
|
|
717
|
+
gaps.append({
|
|
718
|
+
"gap_type": "test_without_control",
|
|
719
|
+
"description": (
|
|
720
|
+
f"Code '{code_id}' has test '{test_id}', "
|
|
721
|
+
f"but neither is linked to a NIST control"
|
|
722
|
+
),
|
|
723
|
+
"code_id": code_id,
|
|
724
|
+
"test_id": test_id,
|
|
725
|
+
"missing_link": "code_module/test_file -> nist_control",
|
|
726
|
+
})
|
|
727
|
+
|
|
728
|
+
conn.close()
|
|
729
|
+
return {
|
|
730
|
+
"total_gaps": len(gaps),
|
|
731
|
+
"gaps": gaps,
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
# ---------------------------------------------------------------------------
|
|
736
|
+
# Auto-link by name matching
|
|
737
|
+
# ---------------------------------------------------------------------------
|
|
738
|
+
def _camel_to_snake(name: str) -> str:
|
|
739
|
+
"""Convert CamelCase to snake_case."""
|
|
740
|
+
s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", name)
|
|
741
|
+
return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
def _snake_to_camel(name: str) -> str:
|
|
745
|
+
"""Convert snake_case to CamelCase."""
|
|
746
|
+
return "".join(word.capitalize() for word in name.split("_"))
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
def auto_link_by_name(project_id: str, db_path=None) -> dict:
|
|
750
|
+
"""Heuristic auto-linking by name matching.
|
|
751
|
+
|
|
752
|
+
1. Match SysML block names to Python class names (case-insensitive,
|
|
753
|
+
underscore/camel conversion)
|
|
754
|
+
2. Match SysML block names to file names (snake_case conversion)
|
|
755
|
+
3. Match requirement IDs found in code comments to DOORS requirements
|
|
756
|
+
4. Match requirement IDs found in test names to DOORS requirements
|
|
757
|
+
|
|
758
|
+
Creates links with confidence 0.7 and evidence="auto_linked_by_name_match"
|
|
759
|
+
"""
|
|
760
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
761
|
+
c = conn.cursor()
|
|
762
|
+
matches = []
|
|
763
|
+
links_created = 0
|
|
764
|
+
|
|
765
|
+
# 1. Get all SysML blocks for this project
|
|
766
|
+
c.execute(
|
|
767
|
+
"""SELECT id, name FROM sysml_elements
|
|
768
|
+
WHERE project_id = ? AND element_type = 'block'""",
|
|
769
|
+
(project_id,),
|
|
770
|
+
)
|
|
771
|
+
blocks = c.fetchall()
|
|
772
|
+
|
|
773
|
+
# 2. Get all code mappings for this project (model_code_mappings)
|
|
774
|
+
c.execute(
|
|
775
|
+
"""SELECT code_path, code_type FROM model_code_mappings
|
|
776
|
+
WHERE project_id = ?""",
|
|
777
|
+
(project_id,),
|
|
778
|
+
)
|
|
779
|
+
code_paths = c.fetchall()
|
|
780
|
+
|
|
781
|
+
# 3. Get all DOORS requirements
|
|
782
|
+
c.execute(
|
|
783
|
+
"""SELECT id, doors_id, title FROM doors_requirements
|
|
784
|
+
WHERE project_id = ?""",
|
|
785
|
+
(project_id,),
|
|
786
|
+
)
|
|
787
|
+
requirements = c.fetchall()
|
|
788
|
+
|
|
789
|
+
# Strategy 1 & 2: Match SysML block names to code paths
|
|
790
|
+
for block_id, block_name in blocks:
|
|
791
|
+
snake_name = _camel_to_snake(block_name)
|
|
792
|
+
lower_name = block_name.lower()
|
|
793
|
+
|
|
794
|
+
for code_path_row in code_paths:
|
|
795
|
+
code_path = code_path_row[0]
|
|
796
|
+
file_stem = Path(code_path).stem.lower()
|
|
797
|
+
Path(code_path).name.lower()
|
|
798
|
+
|
|
799
|
+
# Match block name (snake_case) to file stem
|
|
800
|
+
if snake_name == file_stem or lower_name == file_stem:
|
|
801
|
+
result = create_link(
|
|
802
|
+
project_id=project_id,
|
|
803
|
+
source_type="sysml_element",
|
|
804
|
+
source_id=block_id,
|
|
805
|
+
target_type="code_module",
|
|
806
|
+
target_id=code_path,
|
|
807
|
+
link_type="implements",
|
|
808
|
+
confidence=0.7,
|
|
809
|
+
evidence="auto_linked_by_name_match",
|
|
810
|
+
created_by="icdev-auto-linker",
|
|
811
|
+
db_path=db_path,
|
|
812
|
+
)
|
|
813
|
+
if result.get("created") or result.get("id"):
|
|
814
|
+
links_created += 1
|
|
815
|
+
matches.append({
|
|
816
|
+
"type": "block_to_code",
|
|
817
|
+
"block_id": block_id,
|
|
818
|
+
"block_name": block_name,
|
|
819
|
+
"code_path": code_path,
|
|
820
|
+
"match_method": "name_match",
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
# Match CamelCase block name to file name containing it
|
|
824
|
+
camel_lower = block_name.lower().replace("_", "")
|
|
825
|
+
stem_lower = file_stem.replace("_", "")
|
|
826
|
+
if camel_lower == stem_lower and camel_lower:
|
|
827
|
+
result = create_link(
|
|
828
|
+
project_id=project_id,
|
|
829
|
+
source_type="sysml_element",
|
|
830
|
+
source_id=block_id,
|
|
831
|
+
target_type="code_module",
|
|
832
|
+
target_id=code_path,
|
|
833
|
+
link_type="implements",
|
|
834
|
+
confidence=0.7,
|
|
835
|
+
evidence="auto_linked_by_name_match",
|
|
836
|
+
created_by="icdev-auto-linker",
|
|
837
|
+
db_path=db_path,
|
|
838
|
+
)
|
|
839
|
+
if result.get("created") or result.get("id"):
|
|
840
|
+
links_created += 1
|
|
841
|
+
matches.append({
|
|
842
|
+
"type": "block_to_code_camel",
|
|
843
|
+
"block_id": block_id,
|
|
844
|
+
"block_name": block_name,
|
|
845
|
+
"code_path": code_path,
|
|
846
|
+
"match_method": "camel_case_match",
|
|
847
|
+
})
|
|
848
|
+
|
|
849
|
+
# Strategy 3 & 4: Match requirement IDs in code/test paths
|
|
850
|
+
# Build a lookup of doors_id -> internal id
|
|
851
|
+
req_lookup = {}
|
|
852
|
+
for req_id, doors_id, title in requirements:
|
|
853
|
+
req_lookup[doors_id.lower()] = req_id
|
|
854
|
+
# Also try matching the internal id
|
|
855
|
+
req_lookup[req_id.lower()] = req_id
|
|
856
|
+
|
|
857
|
+
for code_path_row in code_paths:
|
|
858
|
+
code_path = code_path_row[0]
|
|
859
|
+
code_type = code_path_row[1]
|
|
860
|
+
file_str = Path(code_path).name.lower()
|
|
861
|
+
|
|
862
|
+
for doors_id_lower, req_internal_id in req_lookup.items():
|
|
863
|
+
# Normalize doors_id for filename matching (e.g., REQ-001 -> req_001 or req001)
|
|
864
|
+
normalized = doors_id_lower.replace("-", "_").replace(" ", "_")
|
|
865
|
+
normalized_no_sep = doors_id_lower.replace("-", "").replace("_", "").replace(" ", "")
|
|
866
|
+
|
|
867
|
+
if normalized in file_str or normalized_no_sep in file_str:
|
|
868
|
+
if code_type == "test":
|
|
869
|
+
# Test file references requirement
|
|
870
|
+
result = create_link(
|
|
871
|
+
project_id=project_id,
|
|
872
|
+
source_type="test_file",
|
|
873
|
+
source_id=code_path,
|
|
874
|
+
target_type="doors_requirement",
|
|
875
|
+
target_id=req_internal_id,
|
|
876
|
+
link_type="verifies",
|
|
877
|
+
confidence=0.7,
|
|
878
|
+
evidence="auto_linked_by_name_match",
|
|
879
|
+
created_by="icdev-auto-linker",
|
|
880
|
+
db_path=db_path,
|
|
881
|
+
)
|
|
882
|
+
else:
|
|
883
|
+
# Code module references requirement
|
|
884
|
+
result = create_link(
|
|
885
|
+
project_id=project_id,
|
|
886
|
+
source_type="code_module",
|
|
887
|
+
source_id=code_path,
|
|
888
|
+
target_type="doors_requirement",
|
|
889
|
+
target_id=req_internal_id,
|
|
890
|
+
link_type="implements",
|
|
891
|
+
confidence=0.7,
|
|
892
|
+
evidence="auto_linked_by_name_match",
|
|
893
|
+
created_by="icdev-auto-linker",
|
|
894
|
+
db_path=db_path,
|
|
895
|
+
)
|
|
896
|
+
if result.get("created") or result.get("id"):
|
|
897
|
+
links_created += 1
|
|
898
|
+
matches.append({
|
|
899
|
+
"type": "req_in_filename",
|
|
900
|
+
"code_path": code_path,
|
|
901
|
+
"requirement_id": req_internal_id,
|
|
902
|
+
"doors_id": doors_id_lower,
|
|
903
|
+
"match_method": "requirement_id_in_filename",
|
|
904
|
+
})
|
|
905
|
+
|
|
906
|
+
conn.close()
|
|
907
|
+
return {
|
|
908
|
+
"links_created": links_created,
|
|
909
|
+
"matches": matches,
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
# ---------------------------------------------------------------------------
|
|
914
|
+
# Auto-link to NIST controls by keyword
|
|
915
|
+
# ---------------------------------------------------------------------------
|
|
916
|
+
def auto_link_to_controls(project_id: str, db_path=None) -> dict:
|
|
917
|
+
"""Auto-map model elements to NIST controls by element type/stereotype.
|
|
918
|
+
|
|
919
|
+
- Elements with 'auth', 'access', 'login' -> AC family
|
|
920
|
+
- Elements with 'audit', 'log' -> AU family
|
|
921
|
+
- Elements with 'encrypt', 'crypto', 'tls' -> SC family
|
|
922
|
+
- Elements with 'identity', 'authenticate' -> IA family
|
|
923
|
+
|
|
924
|
+
Creates links with confidence 0.6 and evidence="auto_linked_by_keyword_match"
|
|
925
|
+
"""
|
|
926
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
927
|
+
c = conn.cursor()
|
|
928
|
+
mappings = []
|
|
929
|
+
links_created = 0
|
|
930
|
+
|
|
931
|
+
# Get all SysML elements for this project
|
|
932
|
+
c.execute(
|
|
933
|
+
"""SELECT id, name, element_type, stereotype, description
|
|
934
|
+
FROM sysml_elements WHERE project_id = ?""",
|
|
935
|
+
(project_id,),
|
|
936
|
+
)
|
|
937
|
+
elements = c.fetchall()
|
|
938
|
+
|
|
939
|
+
# Get available NIST controls for this project
|
|
940
|
+
c.execute(
|
|
941
|
+
"""SELECT DISTINCT pc.control_id, cc.family, cc.title
|
|
942
|
+
FROM project_controls pc
|
|
943
|
+
JOIN compliance_controls cc ON pc.control_id = cc.id
|
|
944
|
+
WHERE pc.project_id = ?""",
|
|
945
|
+
(project_id,),
|
|
946
|
+
)
|
|
947
|
+
controls = c.fetchall()
|
|
948
|
+
|
|
949
|
+
# Build family -> control_ids lookup
|
|
950
|
+
family_controls = {}
|
|
951
|
+
for ctrl_id, family, title in controls:
|
|
952
|
+
family_controls.setdefault(family, []).append((ctrl_id, title))
|
|
953
|
+
|
|
954
|
+
for elem_id, elem_name, elem_type, stereotype, description in elements:
|
|
955
|
+
# Combine searchable text
|
|
956
|
+
search_text = " ".join(
|
|
957
|
+
s.lower() for s in [elem_name or "", stereotype or "", description or ""]
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
for family, keywords in CONTROL_KEYWORD_MAP.items():
|
|
961
|
+
if family not in family_controls:
|
|
962
|
+
continue
|
|
963
|
+
|
|
964
|
+
matched_keywords = [kw for kw in keywords if kw in search_text]
|
|
965
|
+
if not matched_keywords:
|
|
966
|
+
continue
|
|
967
|
+
|
|
968
|
+
# Link to all controls in the matched family
|
|
969
|
+
for ctrl_id, ctrl_title in family_controls[family]:
|
|
970
|
+
result = create_link(
|
|
971
|
+
project_id=project_id,
|
|
972
|
+
source_type="sysml_element",
|
|
973
|
+
source_id=elem_id,
|
|
974
|
+
target_type="nist_control",
|
|
975
|
+
target_id=ctrl_id,
|
|
976
|
+
link_type="maps_to",
|
|
977
|
+
confidence=0.6,
|
|
978
|
+
evidence="auto_linked_by_keyword_match",
|
|
979
|
+
created_by="icdev-auto-linker",
|
|
980
|
+
db_path=db_path,
|
|
981
|
+
)
|
|
982
|
+
if result.get("created") or result.get("id"):
|
|
983
|
+
links_created += 1
|
|
984
|
+
mappings.append({
|
|
985
|
+
"element_id": elem_id,
|
|
986
|
+
"element_name": elem_name,
|
|
987
|
+
"control_id": ctrl_id,
|
|
988
|
+
"control_title": ctrl_title,
|
|
989
|
+
"control_family": family,
|
|
990
|
+
"matched_keywords": matched_keywords,
|
|
991
|
+
})
|
|
992
|
+
|
|
993
|
+
conn.close()
|
|
994
|
+
return {
|
|
995
|
+
"links_created": links_created,
|
|
996
|
+
"mappings": mappings,
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
# ---------------------------------------------------------------------------
|
|
1001
|
+
# Traceability report (CUI-marked markdown)
|
|
1002
|
+
# ---------------------------------------------------------------------------
|
|
1003
|
+
def generate_traceability_report(project_id: str, db_path=None) -> str:
|
|
1004
|
+
"""Generate full digital thread report as CUI-marked markdown.
|
|
1005
|
+
|
|
1006
|
+
Includes coverage summary, orphan analysis, gap analysis,
|
|
1007
|
+
complete trace chains, and recommendations.
|
|
1008
|
+
"""
|
|
1009
|
+
timestamp = datetime.now().isoformat()
|
|
1010
|
+
|
|
1011
|
+
coverage = compute_coverage(project_id, db_path=db_path)
|
|
1012
|
+
orphans = find_orphans(project_id, db_path=db_path)
|
|
1013
|
+
gaps = find_gaps(project_id, db_path=db_path)
|
|
1014
|
+
integrity = validate_thread_integrity(project_id, db_path=db_path)
|
|
1015
|
+
|
|
1016
|
+
# Collect all trace chains from requirements
|
|
1017
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
1018
|
+
c = conn.cursor()
|
|
1019
|
+
c.execute("SELECT id, doors_id, title FROM doors_requirements WHERE project_id = ?",
|
|
1020
|
+
(project_id,))
|
|
1021
|
+
all_reqs = c.fetchall()
|
|
1022
|
+
|
|
1023
|
+
c.execute("SELECT COUNT(*) FROM digital_thread_links WHERE project_id = ?", (project_id,))
|
|
1024
|
+
total_links = c.fetchone()[0]
|
|
1025
|
+
conn.close()
|
|
1026
|
+
|
|
1027
|
+
# Build report
|
|
1028
|
+
lines = [
|
|
1029
|
+
"CUI // SP-CTI",
|
|
1030
|
+
"",
|
|
1031
|
+
"# Digital Thread Traceability Report",
|
|
1032
|
+
"",
|
|
1033
|
+
f"**Project:** {project_id}",
|
|
1034
|
+
f"**Generated:** {timestamp}",
|
|
1035
|
+
"**Classification:** CUI // SP-CTI",
|
|
1036
|
+
"",
|
|
1037
|
+
"---",
|
|
1038
|
+
"",
|
|
1039
|
+
"## 1. Coverage Summary",
|
|
1040
|
+
"",
|
|
1041
|
+
"| Metric | Value |",
|
|
1042
|
+
"|--------|-------|",
|
|
1043
|
+
f"| Requirement Coverage | {coverage['requirement_coverage']}% |",
|
|
1044
|
+
f"| Model Coverage | {coverage['model_coverage']}% |",
|
|
1045
|
+
f"| Test Coverage | {coverage['test_coverage']}% |",
|
|
1046
|
+
f"| Control Coverage | {coverage['control_coverage']}% |",
|
|
1047
|
+
f"| Overall Thread Completeness | {coverage['overall_thread_completeness']}% |",
|
|
1048
|
+
f"| Total Digital Thread Links | {total_links} |",
|
|
1049
|
+
"",
|
|
1050
|
+
"### Coverage Details",
|
|
1051
|
+
"",
|
|
1052
|
+
]
|
|
1053
|
+
|
|
1054
|
+
details = coverage.get("details", {})
|
|
1055
|
+
lines.append(f"- Requirements: {details.get('requirements_linked', 0)}/{details.get('total_requirements', 0)} linked to models")
|
|
1056
|
+
lines.append(f"- Model Blocks: {details.get('blocks_linked', 0)}/{details.get('total_blocks', 0)} linked to code")
|
|
1057
|
+
lines.append(f"- Code Modules: {details.get('code_with_tests', 0)}/{details.get('total_code_modules', 0)} linked to tests")
|
|
1058
|
+
lines.append(f"- Controls: {details.get('controls_linked', 0)}/{details.get('total_controls', 0)} linked to thread")
|
|
1059
|
+
lines.append(f"- Full Chain (req->model->code->test->control): {details.get('full_chain_requirements', 0)}/{details.get('total_requirements', 0)}")
|
|
1060
|
+
lines.append("")
|
|
1061
|
+
|
|
1062
|
+
# Orphan analysis
|
|
1063
|
+
lines.append("---")
|
|
1064
|
+
lines.append("")
|
|
1065
|
+
lines.append("## 2. Orphan Analysis")
|
|
1066
|
+
lines.append("")
|
|
1067
|
+
|
|
1068
|
+
total_orphans = (
|
|
1069
|
+
orphans["requirements_without_model"]["count"]
|
|
1070
|
+
+ orphans["blocks_without_code"]["count"]
|
|
1071
|
+
+ orphans["code_without_tests"]["count"]
|
|
1072
|
+
+ orphans["controls_without_evidence"]["count"]
|
|
1073
|
+
)
|
|
1074
|
+
lines.append(f"**Total orphaned elements:** {total_orphans}")
|
|
1075
|
+
lines.append("")
|
|
1076
|
+
|
|
1077
|
+
lines.append(f"### Requirements Without Model ({orphans['requirements_without_model']['count']})")
|
|
1078
|
+
if orphans["requirements_without_model"]["items"]:
|
|
1079
|
+
for item in orphans["requirements_without_model"]["items"]:
|
|
1080
|
+
lines.append(f"- `{item.get('doors_id', item['id'])}`: {item.get('title', 'N/A')}")
|
|
1081
|
+
else:
|
|
1082
|
+
lines.append("- None (all requirements traced)")
|
|
1083
|
+
lines.append("")
|
|
1084
|
+
|
|
1085
|
+
lines.append(f"### Blocks Without Code ({orphans['blocks_without_code']['count']})")
|
|
1086
|
+
if orphans["blocks_without_code"]["items"]:
|
|
1087
|
+
for item in orphans["blocks_without_code"]["items"]:
|
|
1088
|
+
lines.append(f"- `{item['id']}`: {item.get('name', 'N/A')}")
|
|
1089
|
+
else:
|
|
1090
|
+
lines.append("- None (all blocks traced)")
|
|
1091
|
+
lines.append("")
|
|
1092
|
+
|
|
1093
|
+
lines.append(f"### Code Without Tests ({orphans['code_without_tests']['count']})")
|
|
1094
|
+
if orphans["code_without_tests"]["items"]:
|
|
1095
|
+
for item in orphans["code_without_tests"]["items"]:
|
|
1096
|
+
lines.append(f"- `{item['id']}`")
|
|
1097
|
+
else:
|
|
1098
|
+
lines.append("- None (all code modules have tests)")
|
|
1099
|
+
lines.append("")
|
|
1100
|
+
|
|
1101
|
+
lines.append(f"### Controls Without Evidence ({orphans['controls_without_evidence']['count']})")
|
|
1102
|
+
if orphans["controls_without_evidence"]["items"]:
|
|
1103
|
+
for item in orphans["controls_without_evidence"]["items"]:
|
|
1104
|
+
lines.append(f"- `{item['control_id']}`")
|
|
1105
|
+
else:
|
|
1106
|
+
lines.append("- None (all controls have thread links)")
|
|
1107
|
+
lines.append("")
|
|
1108
|
+
|
|
1109
|
+
# Gap analysis
|
|
1110
|
+
lines.append("---")
|
|
1111
|
+
lines.append("")
|
|
1112
|
+
lines.append("## 3. Gap Analysis")
|
|
1113
|
+
lines.append("")
|
|
1114
|
+
lines.append(f"**Total gaps found:** {gaps['total_gaps']}")
|
|
1115
|
+
lines.append("")
|
|
1116
|
+
|
|
1117
|
+
if gaps["gaps"]:
|
|
1118
|
+
for i, gap in enumerate(gaps["gaps"], 1):
|
|
1119
|
+
lines.append(f"**Gap {i}** ({gap['gap_type']}): {gap['description']}")
|
|
1120
|
+
lines.append(f" - Missing link: `{gap['missing_link']}`")
|
|
1121
|
+
lines.append("")
|
|
1122
|
+
else:
|
|
1123
|
+
lines.append("No gaps detected. All chains are complete.")
|
|
1124
|
+
lines.append("")
|
|
1125
|
+
|
|
1126
|
+
# Trace chains
|
|
1127
|
+
lines.append("---")
|
|
1128
|
+
lines.append("")
|
|
1129
|
+
lines.append("## 4. Requirement Trace Chains")
|
|
1130
|
+
lines.append("")
|
|
1131
|
+
|
|
1132
|
+
for req_id, doors_id, title in all_reqs[:50]: # Limit to 50 for report size
|
|
1133
|
+
trace = get_forward_trace(project_id, "doors_requirement", req_id, max_depth=5, db_path=db_path)
|
|
1134
|
+
chain_depth = _count_chain_depth(trace.get("links", []))
|
|
1135
|
+
status = "COMPLETE" if chain_depth >= 4 else f"PARTIAL (depth {chain_depth})"
|
|
1136
|
+
lines.append(f"- **{doors_id}** ({title}): {status}")
|
|
1137
|
+
|
|
1138
|
+
if len(all_reqs) > 50:
|
|
1139
|
+
lines.append(f" - ... and {len(all_reqs) - 50} more requirements")
|
|
1140
|
+
lines.append("")
|
|
1141
|
+
|
|
1142
|
+
# Integrity
|
|
1143
|
+
lines.append("---")
|
|
1144
|
+
lines.append("")
|
|
1145
|
+
lines.append("## 5. Thread Integrity")
|
|
1146
|
+
lines.append("")
|
|
1147
|
+
lines.append(f"**Valid:** {'YES' if integrity['valid'] else 'NO'}")
|
|
1148
|
+
lines.append(f"**Issues found:** {len(integrity['issues'])}")
|
|
1149
|
+
lines.append("")
|
|
1150
|
+
|
|
1151
|
+
if integrity["issues"]:
|
|
1152
|
+
for issue in integrity["issues"][:20]:
|
|
1153
|
+
lines.append(f"- [{issue['severity']}] {issue['description']}")
|
|
1154
|
+
if len(integrity["issues"]) > 20:
|
|
1155
|
+
lines.append(f"- ... and {len(integrity['issues']) - 20} more issues")
|
|
1156
|
+
lines.append("")
|
|
1157
|
+
|
|
1158
|
+
# Recommendations
|
|
1159
|
+
lines.append("---")
|
|
1160
|
+
lines.append("")
|
|
1161
|
+
lines.append("## 6. Recommendations")
|
|
1162
|
+
lines.append("")
|
|
1163
|
+
|
|
1164
|
+
recommendations = []
|
|
1165
|
+
if coverage["requirement_coverage"] < 100:
|
|
1166
|
+
recommendations.append(
|
|
1167
|
+
f"Link remaining {details.get('total_requirements', 0) - details.get('requirements_linked', 0)} "
|
|
1168
|
+
f"requirements to SysML model elements"
|
|
1169
|
+
)
|
|
1170
|
+
if coverage["model_coverage"] < 100:
|
|
1171
|
+
recommendations.append(
|
|
1172
|
+
f"Map remaining {details.get('total_blocks', 0) - details.get('blocks_linked', 0)} "
|
|
1173
|
+
f"SysML blocks to code modules"
|
|
1174
|
+
)
|
|
1175
|
+
if coverage["test_coverage"] < 100:
|
|
1176
|
+
recommendations.append(
|
|
1177
|
+
f"Write tests for {details.get('total_code_modules', 0) - details.get('code_with_tests', 0)} "
|
|
1178
|
+
f"untested code modules"
|
|
1179
|
+
)
|
|
1180
|
+
if coverage["control_coverage"] < 100:
|
|
1181
|
+
recommendations.append(
|
|
1182
|
+
f"Map remaining {details.get('total_controls', 0) - details.get('controls_linked', 0)} "
|
|
1183
|
+
f"NIST controls to thread elements"
|
|
1184
|
+
)
|
|
1185
|
+
if not integrity["valid"]:
|
|
1186
|
+
recommendations.append("Resolve thread integrity issues before ATO submission")
|
|
1187
|
+
if gaps["total_gaps"] > 0:
|
|
1188
|
+
recommendations.append(f"Address {gaps['total_gaps']} chain gaps to improve completeness")
|
|
1189
|
+
|
|
1190
|
+
if recommendations:
|
|
1191
|
+
for i, rec in enumerate(recommendations, 1):
|
|
1192
|
+
lines.append(f"{i}. {rec}")
|
|
1193
|
+
else:
|
|
1194
|
+
lines.append("No recommendations. Digital thread is complete.")
|
|
1195
|
+
lines.append("")
|
|
1196
|
+
|
|
1197
|
+
lines.append("---")
|
|
1198
|
+
lines.append("")
|
|
1199
|
+
lines.append("CUI // SP-CTI")
|
|
1200
|
+
|
|
1201
|
+
return "\n".join(lines)
|
|
1202
|
+
|
|
1203
|
+
|
|
1204
|
+
def _count_chain_depth(links: list, depth: int = 0) -> int:
|
|
1205
|
+
"""Count the maximum depth of a trace chain."""
|
|
1206
|
+
if not links:
|
|
1207
|
+
return depth
|
|
1208
|
+
max_d = depth
|
|
1209
|
+
for link in links:
|
|
1210
|
+
child_depth = _count_chain_depth(link.get("children", []), depth + 1)
|
|
1211
|
+
if child_depth > max_d:
|
|
1212
|
+
max_d = child_depth
|
|
1213
|
+
return max_d
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
# ---------------------------------------------------------------------------
|
|
1217
|
+
# Thread integrity validation
|
|
1218
|
+
# ---------------------------------------------------------------------------
|
|
1219
|
+
def validate_thread_integrity(project_id: str, db_path=None) -> dict:
|
|
1220
|
+
"""Check for data integrity issues.
|
|
1221
|
+
|
|
1222
|
+
- Broken links (source/target IDs that don't exist in their tables)
|
|
1223
|
+
- Circular references
|
|
1224
|
+
- Duplicate links
|
|
1225
|
+
- Invalid types
|
|
1226
|
+
"""
|
|
1227
|
+
conn = get_connection(db_path=db_path, row_factory=True)
|
|
1228
|
+
c = conn.cursor()
|
|
1229
|
+
issues = []
|
|
1230
|
+
|
|
1231
|
+
# Get all links for this project
|
|
1232
|
+
c.execute(
|
|
1233
|
+
"""SELECT id, source_type, source_id, target_type, target_id, link_type, confidence
|
|
1234
|
+
FROM digital_thread_links WHERE project_id = ?""",
|
|
1235
|
+
(project_id,),
|
|
1236
|
+
)
|
|
1237
|
+
all_links = c.fetchall()
|
|
1238
|
+
|
|
1239
|
+
# Check 1: Invalid types
|
|
1240
|
+
for link_id, src_type, src_id, tgt_type, tgt_id, ltype, conf in all_links:
|
|
1241
|
+
if src_type not in VALID_TYPES:
|
|
1242
|
+
issues.append({
|
|
1243
|
+
"severity": "error",
|
|
1244
|
+
"type": "invalid_source_type",
|
|
1245
|
+
"link_id": link_id,
|
|
1246
|
+
"description": f"Link {link_id}: invalid source_type '{src_type}'",
|
|
1247
|
+
})
|
|
1248
|
+
if tgt_type not in VALID_TYPES:
|
|
1249
|
+
issues.append({
|
|
1250
|
+
"severity": "error",
|
|
1251
|
+
"type": "invalid_target_type",
|
|
1252
|
+
"link_id": link_id,
|
|
1253
|
+
"description": f"Link {link_id}: invalid target_type '{tgt_type}'",
|
|
1254
|
+
})
|
|
1255
|
+
if ltype not in VALID_LINK_TYPES:
|
|
1256
|
+
issues.append({
|
|
1257
|
+
"severity": "error",
|
|
1258
|
+
"type": "invalid_link_type",
|
|
1259
|
+
"link_id": link_id,
|
|
1260
|
+
"description": f"Link {link_id}: invalid link_type '{ltype}'",
|
|
1261
|
+
})
|
|
1262
|
+
|
|
1263
|
+
# Check 2: Broken links (source/target IDs not in their tables)
|
|
1264
|
+
type_table_map = {
|
|
1265
|
+
"doors_requirement": ("doors_requirements", "id"),
|
|
1266
|
+
"sysml_element": ("sysml_elements", "id"),
|
|
1267
|
+
"nist_control": ("compliance_controls", "id"),
|
|
1268
|
+
"stig_rule": ("stig_findings", "rule_id"),
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
for link_id, src_type, src_id, tgt_type, tgt_id, ltype, conf in all_links:
|
|
1272
|
+
# Check source exists
|
|
1273
|
+
if src_type in type_table_map:
|
|
1274
|
+
table, col = type_table_map[src_type]
|
|
1275
|
+
try:
|
|
1276
|
+
c.execute(f"SELECT 1 FROM {table} WHERE {col} = ? LIMIT 1", (src_id,))
|
|
1277
|
+
if not c.fetchone():
|
|
1278
|
+
issues.append({
|
|
1279
|
+
"severity": "warning",
|
|
1280
|
+
"type": "broken_source_link",
|
|
1281
|
+
"link_id": link_id,
|
|
1282
|
+
"description": (
|
|
1283
|
+
f"Link {link_id}: source {src_type} '{src_id}' "
|
|
1284
|
+
f"not found in {table}"
|
|
1285
|
+
),
|
|
1286
|
+
})
|
|
1287
|
+
except Exception:
|
|
1288
|
+
pass # Table might not exist
|
|
1289
|
+
|
|
1290
|
+
# Check target exists
|
|
1291
|
+
if tgt_type in type_table_map:
|
|
1292
|
+
table, col = type_table_map[tgt_type]
|
|
1293
|
+
try:
|
|
1294
|
+
c.execute(f"SELECT 1 FROM {table} WHERE {col} = ? LIMIT 1", (tgt_id,))
|
|
1295
|
+
if not c.fetchone():
|
|
1296
|
+
issues.append({
|
|
1297
|
+
"severity": "warning",
|
|
1298
|
+
"type": "broken_target_link",
|
|
1299
|
+
"link_id": link_id,
|
|
1300
|
+
"description": (
|
|
1301
|
+
f"Link {link_id}: target {tgt_type} '{tgt_id}' "
|
|
1302
|
+
f"not found in {table}"
|
|
1303
|
+
),
|
|
1304
|
+
})
|
|
1305
|
+
except Exception:
|
|
1306
|
+
pass # Table might not exist
|
|
1307
|
+
|
|
1308
|
+
# Check 3: Circular references (detect cycles using DFS)
|
|
1309
|
+
# Build adjacency list
|
|
1310
|
+
adj = {}
|
|
1311
|
+
for link_id, src_type, src_id, tgt_type, tgt_id, ltype, conf in all_links:
|
|
1312
|
+
src_key = (src_type, src_id)
|
|
1313
|
+
tgt_key = (tgt_type, tgt_id)
|
|
1314
|
+
adj.setdefault(src_key, []).append((tgt_key, link_id))
|
|
1315
|
+
|
|
1316
|
+
visited = set()
|
|
1317
|
+
in_stack = set()
|
|
1318
|
+
cycle_links = set()
|
|
1319
|
+
|
|
1320
|
+
def dfs(node):
|
|
1321
|
+
visited.add(node)
|
|
1322
|
+
in_stack.add(node)
|
|
1323
|
+
for neighbor, lid in adj.get(node, []):
|
|
1324
|
+
if neighbor in in_stack:
|
|
1325
|
+
cycle_links.add(lid)
|
|
1326
|
+
elif neighbor not in visited:
|
|
1327
|
+
dfs(neighbor)
|
|
1328
|
+
in_stack.discard(node)
|
|
1329
|
+
|
|
1330
|
+
for node in adj:
|
|
1331
|
+
if node not in visited:
|
|
1332
|
+
dfs(node)
|
|
1333
|
+
|
|
1334
|
+
for lid in cycle_links:
|
|
1335
|
+
issues.append({
|
|
1336
|
+
"severity": "warning",
|
|
1337
|
+
"type": "circular_reference",
|
|
1338
|
+
"link_id": lid,
|
|
1339
|
+
"description": f"Link {lid}: participates in a circular reference chain",
|
|
1340
|
+
})
|
|
1341
|
+
|
|
1342
|
+
# Check 4: Duplicate links (same source+target+link_type, different IDs)
|
|
1343
|
+
# The UNIQUE constraint should prevent this, but check anyway
|
|
1344
|
+
seen_combos = {}
|
|
1345
|
+
for link_id, src_type, src_id, tgt_type, tgt_id, ltype, conf in all_links:
|
|
1346
|
+
combo = (src_type, src_id, tgt_type, tgt_id, ltype)
|
|
1347
|
+
if combo in seen_combos:
|
|
1348
|
+
issues.append({
|
|
1349
|
+
"severity": "info",
|
|
1350
|
+
"type": "duplicate_link",
|
|
1351
|
+
"link_id": link_id,
|
|
1352
|
+
"description": (
|
|
1353
|
+
f"Link {link_id}: duplicate of link {seen_combos[combo]} "
|
|
1354
|
+
f"({src_type}:{src_id} -> {tgt_type}:{tgt_id} [{ltype}])"
|
|
1355
|
+
),
|
|
1356
|
+
})
|
|
1357
|
+
else:
|
|
1358
|
+
seen_combos[combo] = link_id
|
|
1359
|
+
|
|
1360
|
+
conn.close()
|
|
1361
|
+
valid = all(issue["severity"] != "error" for issue in issues)
|
|
1362
|
+
return {
|
|
1363
|
+
"valid": valid,
|
|
1364
|
+
"total_links": len(all_links),
|
|
1365
|
+
"issues_count": len(issues),
|
|
1366
|
+
"issues": issues,
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
|
|
1370
|
+
# ---------------------------------------------------------------------------
|
|
1371
|
+
# CLI entry point
|
|
1372
|
+
# ---------------------------------------------------------------------------
|
|
1373
|
+
def main():
|
|
1374
|
+
parser = argparse.ArgumentParser(
|
|
1375
|
+
description="ICDEV Digital Thread Engine -- end-to-end traceability"
|
|
1376
|
+
)
|
|
1377
|
+
parser.add_argument("--project-id", required=True, help="Project identifier")
|
|
1378
|
+
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
1379
|
+
parser.add_argument("--db-path", type=Path, help="Override database path")
|
|
1380
|
+
|
|
1381
|
+
sub = parser.add_subparsers(dest="command")
|
|
1382
|
+
|
|
1383
|
+
# create-link
|
|
1384
|
+
link_p = sub.add_parser("create-link", help="Create a digital thread link")
|
|
1385
|
+
link_p.add_argument("--source-type", required=True, choices=VALID_TYPES,
|
|
1386
|
+
help="Source element type")
|
|
1387
|
+
link_p.add_argument("--source-id", required=True, help="Source element ID")
|
|
1388
|
+
link_p.add_argument("--target-type", required=True, choices=VALID_TYPES,
|
|
1389
|
+
help="Target element type")
|
|
1390
|
+
link_p.add_argument("--target-id", required=True, help="Target element ID")
|
|
1391
|
+
link_p.add_argument("--link-type", required=True, choices=VALID_LINK_TYPES,
|
|
1392
|
+
help="Relationship type")
|
|
1393
|
+
link_p.add_argument("--evidence", help="Evidence for the link")
|
|
1394
|
+
link_p.add_argument("--confidence", type=float, default=1.0,
|
|
1395
|
+
help="Confidence score 0.0-1.0 (default: 1.0)")
|
|
1396
|
+
|
|
1397
|
+
# delete-link
|
|
1398
|
+
del_p = sub.add_parser("delete-link", help="Delete a digital thread link by ID")
|
|
1399
|
+
del_p.add_argument("--link-id", required=True, type=int, help="Link ID to delete")
|
|
1400
|
+
|
|
1401
|
+
# trace-forward
|
|
1402
|
+
fwd_p = sub.add_parser("trace-forward", help="Forward trace from an element")
|
|
1403
|
+
fwd_p.add_argument("--source-type", required=True, choices=VALID_TYPES,
|
|
1404
|
+
help="Source element type")
|
|
1405
|
+
fwd_p.add_argument("--source-id", required=True, help="Source element ID")
|
|
1406
|
+
fwd_p.add_argument("--max-depth", type=int, default=10,
|
|
1407
|
+
help="Maximum traversal depth (default: 10)")
|
|
1408
|
+
|
|
1409
|
+
# trace-backward
|
|
1410
|
+
bwd_p = sub.add_parser("trace-backward", help="Backward trace to an element")
|
|
1411
|
+
bwd_p.add_argument("--target-type", required=True, choices=VALID_TYPES,
|
|
1412
|
+
help="Target element type")
|
|
1413
|
+
bwd_p.add_argument("--target-id", required=True, help="Target element ID")
|
|
1414
|
+
bwd_p.add_argument("--max-depth", type=int, default=10,
|
|
1415
|
+
help="Maximum traversal depth (default: 10)")
|
|
1416
|
+
|
|
1417
|
+
# full-thread
|
|
1418
|
+
full_p = sub.add_parser("full-thread", help="Bidirectional trace from an element")
|
|
1419
|
+
full_p.add_argument("--element-type", required=True, choices=VALID_TYPES,
|
|
1420
|
+
help="Element type")
|
|
1421
|
+
full_p.add_argument("--element-id", required=True, help="Element ID")
|
|
1422
|
+
|
|
1423
|
+
# coverage
|
|
1424
|
+
sub.add_parser("coverage", help="Compute digital thread coverage metrics")
|
|
1425
|
+
|
|
1426
|
+
# orphans
|
|
1427
|
+
sub.add_parser("orphans", help="Find elements with no links")
|
|
1428
|
+
|
|
1429
|
+
# gaps
|
|
1430
|
+
sub.add_parser("gaps", help="Find missing links in expected chains")
|
|
1431
|
+
|
|
1432
|
+
# auto-link
|
|
1433
|
+
sub.add_parser("auto-link", help="Auto-link elements by name matching")
|
|
1434
|
+
|
|
1435
|
+
# auto-link-controls
|
|
1436
|
+
sub.add_parser("auto-link-controls",
|
|
1437
|
+
help="Auto-map elements to NIST controls by keyword")
|
|
1438
|
+
|
|
1439
|
+
# report
|
|
1440
|
+
sub.add_parser("report", help="Generate full traceability report")
|
|
1441
|
+
|
|
1442
|
+
# validate
|
|
1443
|
+
sub.add_parser("validate", help="Validate thread integrity")
|
|
1444
|
+
|
|
1445
|
+
args = parser.parse_args()
|
|
1446
|
+
db = args.db_path # Storage layer handles default path resolution
|
|
1447
|
+
|
|
1448
|
+
if not args.command:
|
|
1449
|
+
parser.print_help()
|
|
1450
|
+
sys.exit(1)
|
|
1451
|
+
|
|
1452
|
+
print("CUI // SP-CTI")
|
|
1453
|
+
print("")
|
|
1454
|
+
|
|
1455
|
+
if args.command == "create-link":
|
|
1456
|
+
result = create_link(
|
|
1457
|
+
project_id=args.project_id,
|
|
1458
|
+
source_type=args.source_type,
|
|
1459
|
+
source_id=args.source_id,
|
|
1460
|
+
target_type=args.target_type,
|
|
1461
|
+
target_id=args.target_id,
|
|
1462
|
+
link_type=args.link_type,
|
|
1463
|
+
evidence=args.evidence,
|
|
1464
|
+
confidence=args.confidence,
|
|
1465
|
+
db_path=db,
|
|
1466
|
+
)
|
|
1467
|
+
if args.json:
|
|
1468
|
+
print(json.dumps(result, indent=2))
|
|
1469
|
+
else:
|
|
1470
|
+
if "error" in result:
|
|
1471
|
+
print(f"ERROR: {result['error']}")
|
|
1472
|
+
else:
|
|
1473
|
+
print(f"Link created: ID={result['id']}")
|
|
1474
|
+
print(f" {args.source_type}:{args.source_id} -> {args.target_type}:{args.target_id}")
|
|
1475
|
+
print(f" Type: {args.link_type} | Confidence: {args.confidence}")
|
|
1476
|
+
|
|
1477
|
+
elif args.command == "delete-link":
|
|
1478
|
+
deleted = delete_link(args.project_id, args.link_id, db_path=db)
|
|
1479
|
+
if args.json:
|
|
1480
|
+
print(json.dumps({"deleted": deleted}, indent=2))
|
|
1481
|
+
else:
|
|
1482
|
+
if deleted:
|
|
1483
|
+
print(f"Link {args.link_id} deleted.")
|
|
1484
|
+
else:
|
|
1485
|
+
print(f"Link {args.link_id} not found or could not be deleted.")
|
|
1486
|
+
|
|
1487
|
+
elif args.command == "trace-forward":
|
|
1488
|
+
result = get_forward_trace(
|
|
1489
|
+
project_id=args.project_id,
|
|
1490
|
+
source_type=args.source_type,
|
|
1491
|
+
source_id=args.source_id,
|
|
1492
|
+
max_depth=args.max_depth,
|
|
1493
|
+
db_path=db,
|
|
1494
|
+
)
|
|
1495
|
+
if args.json:
|
|
1496
|
+
print(json.dumps(result, indent=2))
|
|
1497
|
+
else:
|
|
1498
|
+
_print_forward_tree(result)
|
|
1499
|
+
|
|
1500
|
+
elif args.command == "trace-backward":
|
|
1501
|
+
result = get_backward_trace(
|
|
1502
|
+
project_id=args.project_id,
|
|
1503
|
+
target_type=args.target_type,
|
|
1504
|
+
target_id=args.target_id,
|
|
1505
|
+
max_depth=args.max_depth,
|
|
1506
|
+
db_path=db,
|
|
1507
|
+
)
|
|
1508
|
+
if args.json:
|
|
1509
|
+
print(json.dumps(result, indent=2))
|
|
1510
|
+
else:
|
|
1511
|
+
_print_backward_tree(result)
|
|
1512
|
+
|
|
1513
|
+
elif args.command == "full-thread":
|
|
1514
|
+
result = get_full_thread(
|
|
1515
|
+
project_id=args.project_id,
|
|
1516
|
+
element_type=args.element_type,
|
|
1517
|
+
element_id=args.element_id,
|
|
1518
|
+
db_path=db,
|
|
1519
|
+
)
|
|
1520
|
+
if args.json:
|
|
1521
|
+
print(json.dumps(result, indent=2))
|
|
1522
|
+
else:
|
|
1523
|
+
elem = result["element"]
|
|
1524
|
+
print(f"Full Thread: {elem['type']}:{elem['id']} ({elem['name']})")
|
|
1525
|
+
print("")
|
|
1526
|
+
print("--- Forward Trace ---")
|
|
1527
|
+
_print_links(result.get("forward", []), indent=2)
|
|
1528
|
+
print("")
|
|
1529
|
+
print("--- Backward Trace ---")
|
|
1530
|
+
_print_links(result.get("backward", []), indent=2, direction="backward")
|
|
1531
|
+
|
|
1532
|
+
elif args.command == "coverage":
|
|
1533
|
+
result = compute_coverage(args.project_id, db_path=db)
|
|
1534
|
+
if args.json:
|
|
1535
|
+
print(json.dumps(result, indent=2))
|
|
1536
|
+
else:
|
|
1537
|
+
print("Digital Thread Coverage")
|
|
1538
|
+
print("=" * 40)
|
|
1539
|
+
print(f" Requirement Coverage: {result['requirement_coverage']}%")
|
|
1540
|
+
print(f" Model Coverage: {result['model_coverage']}%")
|
|
1541
|
+
print(f" Test Coverage: {result['test_coverage']}%")
|
|
1542
|
+
print(f" Control Coverage: {result['control_coverage']}%")
|
|
1543
|
+
print(f" Overall Completeness: {result['overall_thread_completeness']}%")
|
|
1544
|
+
|
|
1545
|
+
elif args.command == "orphans":
|
|
1546
|
+
result = find_orphans(args.project_id, db_path=db)
|
|
1547
|
+
if args.json:
|
|
1548
|
+
print(json.dumps(result, indent=2))
|
|
1549
|
+
else:
|
|
1550
|
+
print("Orphan Analysis")
|
|
1551
|
+
print("=" * 40)
|
|
1552
|
+
for category, data in result.items():
|
|
1553
|
+
print(f"\n {category}: {data['count']}")
|
|
1554
|
+
for item in data["items"][:10]:
|
|
1555
|
+
label = item.get("title") or item.get("name") or item.get("control_id") or item.get("id")
|
|
1556
|
+
print(f" - {label}")
|
|
1557
|
+
if data["count"] > 10:
|
|
1558
|
+
print(f" ... and {data['count'] - 10} more")
|
|
1559
|
+
|
|
1560
|
+
elif args.command == "gaps":
|
|
1561
|
+
result = find_gaps(args.project_id, db_path=db)
|
|
1562
|
+
if args.json:
|
|
1563
|
+
print(json.dumps(result, indent=2))
|
|
1564
|
+
else:
|
|
1565
|
+
print(f"Gap Analysis: {result['total_gaps']} gaps found")
|
|
1566
|
+
print("=" * 40)
|
|
1567
|
+
for gap in result["gaps"]:
|
|
1568
|
+
print(f"\n [{gap['gap_type']}] {gap['description']}")
|
|
1569
|
+
print(f" Missing: {gap['missing_link']}")
|
|
1570
|
+
|
|
1571
|
+
elif args.command == "auto-link":
|
|
1572
|
+
result = auto_link_by_name(args.project_id, db_path=db)
|
|
1573
|
+
if args.json:
|
|
1574
|
+
print(json.dumps(result, indent=2))
|
|
1575
|
+
else:
|
|
1576
|
+
print(f"Auto-Link by Name: {result['links_created']} links created")
|
|
1577
|
+
for m in result["matches"]:
|
|
1578
|
+
print(f" - [{m.get('match_method', 'unknown')}] {m}")
|
|
1579
|
+
|
|
1580
|
+
elif args.command == "auto-link-controls":
|
|
1581
|
+
result = auto_link_to_controls(args.project_id, db_path=db)
|
|
1582
|
+
if args.json:
|
|
1583
|
+
print(json.dumps(result, indent=2))
|
|
1584
|
+
else:
|
|
1585
|
+
print(f"Auto-Link to Controls: {result['links_created']} links created")
|
|
1586
|
+
for m in result["mappings"]:
|
|
1587
|
+
print(f" - {m['element_name']} -> {m['control_id']} ({m['control_family']})")
|
|
1588
|
+
print(f" Keywords: {', '.join(m['matched_keywords'])}")
|
|
1589
|
+
|
|
1590
|
+
elif args.command == "report":
|
|
1591
|
+
report = generate_traceability_report(args.project_id, db_path=db)
|
|
1592
|
+
print(report)
|
|
1593
|
+
|
|
1594
|
+
elif args.command == "validate":
|
|
1595
|
+
result = validate_thread_integrity(args.project_id, db_path=db)
|
|
1596
|
+
if args.json:
|
|
1597
|
+
print(json.dumps(result, indent=2))
|
|
1598
|
+
else:
|
|
1599
|
+
status = "VALID" if result["valid"] else "INVALID"
|
|
1600
|
+
print(f"Thread Integrity: {status}")
|
|
1601
|
+
print(f" Total links: {result['total_links']}")
|
|
1602
|
+
print(f" Issues: {result['issues_count']}")
|
|
1603
|
+
for issue in result["issues"]:
|
|
1604
|
+
print(f" - [{issue['severity']}] {issue['description']}")
|
|
1605
|
+
|
|
1606
|
+
print("")
|
|
1607
|
+
print("CUI // SP-CTI")
|
|
1608
|
+
|
|
1609
|
+
|
|
1610
|
+
# ---------------------------------------------------------------------------
|
|
1611
|
+
# Pretty-print helpers for CLI
|
|
1612
|
+
# ---------------------------------------------------------------------------
|
|
1613
|
+
def _print_forward_tree(result: dict):
|
|
1614
|
+
"""Print a forward trace result as an indented tree."""
|
|
1615
|
+
src = result.get("source", {})
|
|
1616
|
+
print(f"Forward Trace: {src['type']}:{src['id']} ({src.get('name', '')})")
|
|
1617
|
+
print("")
|
|
1618
|
+
_print_links(result.get("links", []), indent=2)
|
|
1619
|
+
|
|
1620
|
+
|
|
1621
|
+
def _print_backward_tree(result: dict):
|
|
1622
|
+
"""Print a backward trace result as an indented tree."""
|
|
1623
|
+
tgt = result.get("target", {})
|
|
1624
|
+
print(f"Backward Trace: {tgt['type']}:{tgt['id']} ({tgt.get('name', '')})")
|
|
1625
|
+
print("")
|
|
1626
|
+
_print_links(result.get("links", []), indent=2, direction="backward")
|
|
1627
|
+
|
|
1628
|
+
|
|
1629
|
+
def _print_links(links: list, indent: int = 0, direction: str = "forward"):
|
|
1630
|
+
"""Recursively print trace links."""
|
|
1631
|
+
prefix = " " * indent
|
|
1632
|
+
for link in links:
|
|
1633
|
+
lt = link.get("link_type", "?")
|
|
1634
|
+
conf = link.get("confidence", 1.0)
|
|
1635
|
+
|
|
1636
|
+
if direction == "forward":
|
|
1637
|
+
target = link.get("target", {})
|
|
1638
|
+
label = f"{target.get('type', '?')}:{target.get('id', '?')} ({target.get('name', '')})"
|
|
1639
|
+
print(f"{prefix}--[{lt} ({conf})]-> {label}")
|
|
1640
|
+
_print_links(link.get("children", []), indent + 4, direction)
|
|
1641
|
+
else:
|
|
1642
|
+
source = link.get("source", {})
|
|
1643
|
+
label = f"{source.get('type', '?')}:{source.get('id', '?')} ({source.get('name', '')})"
|
|
1644
|
+
print(f"{prefix}<-[{lt} ({conf})]-- {label}")
|
|
1645
|
+
_print_links(link.get("children", []), indent + 4, direction)
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
if __name__ == "__main__":
|
|
1649
|
+
main()
|
|
1650
|
+
# [TEMPLATE: CUI // SP-CTI]
|