synapse-hub 2.2.4 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/.agents/agents/failure-analyst.md +11 -0
  2. package/.agents/agents/live-explorer.md +28 -0
  3. package/.agents/agents/orchestrator.md +20 -4
  4. package/.agents/agents/pom-crafter.md +12 -0
  5. package/.agents/agents/quality-gatekeeper.md +19 -4
  6. package/.agents/agents/test-runner.md +11 -0
  7. package/.agents/rules/communication-style.md +40 -0
  8. package/.agents/rules/context-engineering.md +23 -0
  9. package/.agents/rules/git-push-protocol.md +10 -16
  10. package/.agents/rules/graphify.md +14 -14
  11. package/.agents/rules/mandatory-playwright-live.md +29 -29
  12. package/.agents/rules/mobile_appium_rules.md +132 -0
  13. package/.agents/rules/performance_testing_rules.md +74 -0
  14. package/.agents/rules/systematic-debugging.md +6 -6
  15. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +64 -215
  16. package/.agents/skills/agent-orchestration-multi-agent-optimize/examples/swarm_workload_config.json +12 -0
  17. package/.agents/skills/antigravity-test-sentinel/SKILL.md +3 -3
  18. package/.agents/skills/antigravity-test-sentinel/examples/dod_gate_policy.json +11 -0
  19. package/.agents/skills/api-network-mocking-interception/SKILL.md +1 -1
  20. package/.agents/skills/api-network-mocking-interception/examples/network_route_stub.json +9 -0
  21. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +9 -9
  22. package/.agents/skills/api-spec-to-e2e-matrix/examples/sample_openapi.json +19 -0
  23. package/.agents/skills/architecture-decision-records/SKILL.md +35 -412
  24. package/.agents/skills/architecture-decision-records/examples/0001-record-architecture-decisions.md +12 -0
  25. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +1 -1
  26. package/.agents/skills/auth-session-and-cookie-manager/examples/storage_state_sample.json +11 -0
  27. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +80 -186
  28. package/.agents/skills/backend-load-and-stress-patterns/examples/k6_load_test_sample.js +13 -0
  29. package/.agents/skills/bats-testing-patterns/SKILL.md +1 -1
  30. package/.agents/skills/bats-testing-patterns/examples/cli_runner_test.bats +6 -0
  31. package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -614
  32. package/.agents/skills/clean-pom-crafter/SKILL.md +21 -3
  33. package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -28
  34. package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -138
  35. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +2 -2
  36. package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_page_sample.groovy +9 -0
  37. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +5 -5
  38. package/.agents/skills/db-state-verifier-and-seeder/examples/db_fixture_seed.json +12 -0
  39. package/.agents/skills/e2e-testing-patterns/SKILL.md +1 -1
  40. package/.agents/skills/e2e-testing-patterns/examples/playwright_sharded_config.ts +12 -0
  41. package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -522
  42. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +17 -3
  43. package/.agents/skills/failure-autopsy-and-healing/examples/sample_junit_failure.xml +8 -0
  44. package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -64
  45. package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -56
  46. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -98
  47. package/.agents/skills/file-and-report-validator/SKILL.md +1 -1
  48. package/.agents/skills/file-and-report-validator/examples/sample_export_schema.json +14 -0
  49. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +3 -3
  50. package/.agents/skills/flaky-test-sentinel-and-quarantine/examples/quarantine_registry.json +11 -0
  51. package/.agents/skills/gherkin-to-suite/SKILL.md +103 -0
  52. package/.agents/skills/gherkin-to-suite/examples/checkout.feature +39 -0
  53. package/.agents/skills/gherkin-to-suite/examples/suite_report_sample.json +334 -0
  54. package/.agents/skills/github-actions-templates/SKILL.md +47 -304
  55. package/.agents/skills/github-actions-templates/examples/matrix_test_pipeline.yml +8 -0
  56. package/.agents/skills/graphify-impact-analyzer/SKILL.md +6 -6
  57. package/.agents/skills/graphify-impact-analyzer/examples/dependency_blast_radius.json +8 -0
  58. package/.agents/skills/katalon-cli-runner/SKILL.md +2 -2
  59. package/.agents/skills/katalon-cli-runner/examples/headless_kre_exec.sh +3 -0
  60. package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -209
  61. package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -155
  62. package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -67
  63. package/.agents/skills/lam-script-generator/SKILL.md +42 -1
  64. package/.agents/skills/lam-script-generator/examples/user_story_sample.md +8 -0
  65. package/.agents/skills/lean-code-ladder/SKILL.md +83 -0
  66. package/.agents/skills/lean-code-ladder/examples/ladder_walkthrough.md +37 -0
  67. package/.agents/skills/meta-prompt-crafter/SKILL.md +107 -0
  68. package/.agents/skills/meta-prompt-crafter/examples/qa_prompt_template.md +35 -0
  69. package/.agents/skills/persistent-planning-files/SKILL.md +108 -0
  70. package/.agents/skills/persistent-planning-files/examples/task_plan_example.md +56 -0
  71. package/.agents/skills/playwright-live-interaction/SKILL.md +49 -1
  72. package/.agents/skills/playwright-live-interaction/examples/live_probe_script.js +5 -0
  73. package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -160
  74. package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -75
  75. package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -388
  76. package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -134
  77. package/.agents/skills/playwright-webapp-testing/SKILL.md +93 -0
  78. package/.agents/skills/playwright-webapp-testing/examples/server_lifecycle_playwright.py +63 -0
  79. package/.agents/skills/rbt-manual-testing/SKILL.md +150 -0
  80. package/.agents/skills/rbt-manual-testing/examples/checkout_rbt_manual_suite.md +25 -0
  81. package/.agents/skills/security-dast-sentinel/SKILL.md +54 -135
  82. package/.agents/skills/security-dast-sentinel/examples/dast_scan_config.json +10 -0
  83. package/.agents/skills/self-healing-locator/SKILL.md +24 -10
  84. package/.agents/skills/self-healing-locator/examples/aom_diff_sample.json +16 -0
  85. package/.agents/skills/skillspector-security-guard/SKILL.md +32 -90
  86. package/.agents/skills/skillspector-security-guard/examples/safe_mcp_config.json +12 -0
  87. package/.agents/skills/surgical-coding-principles/SKILL.md +89 -0
  88. package/.agents/skills/surgical-coding-principles/examples/surgical_vs_scope_creep.md +46 -0
  89. package/.agents/skills/synthetic-test-data-generator/SKILL.md +3 -3
  90. package/.agents/skills/synthetic-test-data-generator/examples/idempotent_data_recipe.json +9 -0
  91. package/.agents/skills/systematic-debugging/SKILL.md +100 -0
  92. package/.agents/skills/systematic-debugging/examples/four_phase_case.md +44 -0
  93. package/.agents/skills/test-driven-development/SKILL.md +114 -0
  94. package/.agents/skills/test-driven-development/examples/red_green_refactor_qa.md +78 -0
  95. package/.agents/skills/token-and-context-optimizer/SKILL.md +1 -1
  96. package/.agents/skills/token-and-context-optimizer/examples/distilled_log_sample.txt +3 -0
  97. package/.agents/skills/verification-before-completion/SKILL.md +100 -0
  98. package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +46 -0
  99. package/.agents/skills/visual-regression-sentinel/SKILL.md +21 -1
  100. package/.agents/skills/visual-regression-sentinel/examples/visual_mask_rules.json +9 -0
  101. package/.agents/skills/wcag-audit-patterns/SKILL.md +3 -3
  102. package/.agents/skills/wcag-audit-patterns/examples/wcag_compliance_checklist.json +10 -0
  103. package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -541
  104. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +1 -1
  105. package/.agents/skills/web-vitals-and-performance-profiler/examples/cwv_budget_policy.json +6 -0
  106. package/.agents/templates/completion_report.md +57 -57
  107. package/.agents/workflows/analyze_requirement_document.md +18 -0
  108. package/.agents/workflows/export_testcases_excel.md +32 -0
  109. package/.agents/workflows/generate_combinatorial_test_data.md +28 -0
  110. package/.agents/workflows/generate_cross_module_test_plan.md +37 -0
  111. package/.agents/workflows/generate_manual_testcases_rbt.md +56 -0
  112. package/.agents/workflows/generate_requirements_from_website.md +25 -0
  113. package/.agents/workflows/generate_testcases_from_requirements.md +21 -0
  114. package/.agents/workflows/graphify.md +10 -10
  115. package/.agents/workflows/verify.md +2 -2
  116. package/.cursor/rules/language.mdc +7 -7
  117. package/.cursor/rules/synapse-qa.mdc +23 -0
  118. package/.cursorrules +7 -0
  119. package/.pre-commit-hooks.yaml +14 -0
  120. package/.windsurfrules +7 -0
  121. package/AGENTS.md +54 -15
  122. package/CLAUDE.md +35 -12
  123. package/LICENSE +30 -0
  124. package/NOTICE +180 -0
  125. package/README.en.md +325 -0
  126. package/README.md +247 -516
  127. package/action.yml +51 -0
  128. package/benchmarks/qa_eval/README.md +71 -0
  129. package/benchmarks/qa_eval/build_dataset.py +677 -0
  130. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-06_api_contract_drift.json +22 -0
  131. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-07_api_contract_drift.json +22 -0
  132. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-08_api_contract_drift.json +22 -0
  133. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-09_api_contract_drift.json +22 -0
  134. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-10_api_contract_drift.json +22 -0
  135. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-21_fake_pass_tautology.json +21 -0
  136. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-22_fake_pass_tautology.json +21 -0
  137. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-23_fake_pass_tautology.json +21 -0
  138. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-24_fake_pass_tautology.json +21 -0
  139. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-25_fake_pass_tautology.json +21 -0
  140. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-01_locator_drift.json +23 -0
  141. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-02_locator_drift.json +23 -0
  142. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-03_locator_drift.json +23 -0
  143. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-04_locator_drift.json +23 -0
  144. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-05_locator_drift.json +23 -0
  145. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-16_stale_and_mutation.json +22 -0
  146. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-17_stale_and_mutation.json +22 -0
  147. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-18_stale_and_mutation.json +22 -0
  148. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-19_stale_and_mutation.json +22 -0
  149. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-20_stale_and_mutation.json +23 -0
  150. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-11_timing_and_race.json +22 -0
  151. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-12_timing_and_race.json +22 -0
  152. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-13_timing_and_race.json +22 -0
  153. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-14_timing_and_race.json +22 -0
  154. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-15_timing_and_race.json +22 -0
  155. package/benchmarks/qa_eval/eval_runner.py +418 -0
  156. package/benchmarks/qa_eval/reports/AB_METHODOLOGY_REPORT.md +83 -0
  157. package/benchmarks/qa_eval/reports/BENCHMARK_REPORT.md +69 -0
  158. package/benchmarks/qa_eval/reports/eval_results.json +105 -0
  159. package/bin/agent-qa.js +10 -3
  160. package/docs/ARCHITECTURE.md +107 -0
  161. package/docs/CI_CD_INTEGRATION.md +94 -0
  162. package/docs/CLI_REFERENCE.md +157 -0
  163. package/docs/CONTRIBUTING.md +91 -0
  164. package/docs/PLUGIN_DEVELOPMENT_GUIDE.md +126 -0
  165. package/docs/TOKEN_QUOTA_PLAYBOOK.md +41 -0
  166. package/docs/adopted-ecosystem-tools.md +87 -0
  167. package/docs/schemas/synapse.config.schema.json +64 -0
  168. package/examples/cypress-ecommerce-suite/README.md +45 -0
  169. package/examples/cypress-ecommerce-suite/cypress/e2e/checkout.cy.js +17 -0
  170. package/examples/cypress-ecommerce-suite/cypress/pages/BasePage.js +29 -0
  171. package/examples/cypress-ecommerce-suite/cypress/pages/CartPage.js +51 -0
  172. package/examples/cypress-ecommerce-suite/cypress.config.js +13 -0
  173. package/examples/cypress-ecommerce-suite/package.json +13 -0
  174. package/examples/katalon-suite/Keywords/pages/BasePage.groovy +19 -0
  175. package/examples/katalon-suite/Keywords/pages/DashboardPage.groovy +29 -0
  176. package/examples/katalon-suite/Keywords/pages/LoginPage.groovy +45 -0
  177. package/examples/katalon-suite/Profiles/default.glbl +12 -0
  178. package/examples/katalon-suite/README.md +18 -0
  179. package/examples/katalon-suite/Scripts/Auth/TC_LoginSuccess/Script1234567890.groovy +12 -0
  180. package/examples/katalon-suite/Scripts/Dashboard/TC_ViewSummary/Script1234567891.groovy +15 -0
  181. package/examples/katalon-suite/katalon_suite.prj +21 -0
  182. package/examples/playwright-crm-suite/README.md +40 -0
  183. package/examples/playwright-crm-suite/package.json +14 -0
  184. package/examples/playwright-crm-suite/pages/BasePage.ts +37 -0
  185. package/examples/playwright-crm-suite/pages/CustomerPage.ts +57 -0
  186. package/examples/playwright-crm-suite/pages/LoginPage.ts +32 -0
  187. package/examples/playwright-crm-suite/playwright.config.ts +27 -0
  188. package/examples/playwright-crm-suite/tests/auth.spec.ts +22 -0
  189. package/examples/playwright-crm-suite/tests/customers.spec.ts +33 -0
  190. package/examples/plugins/sample-custom-plugin/README.md +14 -0
  191. package/examples/plugins/sample-custom-plugin/plugin.json +16 -0
  192. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/SKILL.md +29 -0
  193. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/examples/audit_fixture.json +8 -0
  194. package/examples/plugins/sample-custom-plugin/tools/custom_security_headers.py +125 -0
  195. package/examples/practices/README.md +22 -0
  196. package/examples/practices/requirements/crm/requirements_contracts.md +292 -0
  197. package/examples/practices/requirements/crm/requirements_customers.md +462 -0
  198. package/examples/practices/requirements/crm/requirements_login.md +202 -0
  199. package/examples/practices/requirements/crm/requirements_projects.md +396 -0
  200. package/examples/practices/requirements/crm/requirements_tasks.md +343 -0
  201. package/examples/practices/requirements/ecommerce/requirements_category_brand.txt +402 -0
  202. package/examples/practices/requirements/jira/KAN-5/KAN-1/2026-04-03_04-28-27.png +0 -0
  203. package/examples/practices/requirements/jira/KAN-5/KAN-1/KAN-1_requirement.md +52 -0
  204. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-19.png +0 -0
  205. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-57.png +0 -0
  206. package/examples/practices/requirements/jira/KAN-5/KAN-4/KAN-4_requirement.md +119 -0
  207. package/examples/practices/requirements/jira/KAN-5/KAN-5_overview.md +9 -0
  208. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-08.png +0 -0
  209. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-23.png +0 -0
  210. package/examples/practices/requirements/jira/KAN-5/KAN-6/KAN-6_requirement.md +32 -0
  211. package/examples/practices/testcases/api/book_management_api_test_cases.md +144 -0
  212. package/examples/practices/testcases/api/user_management_api_test_cases.md +139 -0
  213. package/examples/practices/testcases/crm/test_cases_crm_login.md +148 -0
  214. package/examples/practices/testcases/crm/test_cases_customers.md +156 -0
  215. package/examples/pytest-api-suite/README.md +45 -0
  216. package/examples/pytest-api-suite/conftest.py +31 -0
  217. package/examples/pytest-api-suite/pages/user_api_client.py +36 -0
  218. package/examples/pytest-api-suite/requirements.txt +2 -0
  219. package/examples/pytest-api-suite/tests/test_user_api.py +44 -0
  220. package/examples/selenium-suite/README.md +18 -0
  221. package/examples/selenium-suite/pages/base_page.py +27 -0
  222. package/examples/selenium-suite/pages/login_page.py +29 -0
  223. package/examples/selenium-suite/pages/order_page.py +25 -0
  224. package/examples/selenium-suite/pytest.ini +6 -0
  225. package/examples/selenium-suite/requirements.txt +2 -0
  226. package/examples/selenium-suite/tests/test_login.py +10 -0
  227. package/examples/selenium-suite/tests/test_order.py +10 -0
  228. package/package.json +22 -7
  229. package/scripts/generate_skill_examples.py +244 -0
  230. package/scripts/install.ps1 +112 -0
  231. package/scripts/install_global_plugin.ps1 +94 -0
  232. package/scripts/install_global_plugin.sh +49 -0
  233. package/scripts/lint_skills.ps1 +57 -0
  234. package/scripts/package_dist.py +323 -0
  235. package/scripts/setup.sh +59 -0
  236. package/scripts/update_hub.ps1 +19 -0
  237. package/scripts/update_hub.sh +15 -0
  238. package/skills_manifest.json +209 -6
  239. package/synapse.config.json +30 -0
  240. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +7 -7
  241. package/templates/PROJECT_STATE_TEMPLATE.md +1 -1
  242. package/templates/ai_bridges/CLAUDE.md +33 -16
  243. package/templates/ai_bridges/copilot-instructions.md +14 -21
  244. package/templates/ai_bridges/cursor_synapse_qa.mdc +17 -20
  245. package/templates/ai_bridges/windsurfrules +21 -21
  246. package/templates/ci/github-actions-synapse.yml +71 -4
  247. package/templates/welcome.html.tmpl +1674 -0
  248. package/tools/.infer_baseline.json +1102 -0
  249. package/tools/TOOLS_RUNBOOK.md +92 -130
  250. package/tools/__init__.py +26 -0
  251. package/tools/agent_canary.py +501 -501
  252. package/tools/analyze_project.py +31 -17
  253. package/tools/api_fuzzer.py +278 -6
  254. package/tools/author_integrity.py +6 -6
  255. package/tools/auto_heal.py +1 -1
  256. package/tools/browser_helpers.js +105 -7
  257. package/tools/bug_reporter.py +31 -1
  258. package/tools/bump_version.py +23 -3
  259. package/tools/cache_resolver.py +248 -14
  260. package/tools/check_env.py +55 -14
  261. package/tools/check_version_sync.py +153 -0
  262. package/tools/ci_healing_hook.py +1 -1
  263. package/tools/cli.py +263 -13
  264. package/tools/count_tokens.py +1257 -0
  265. package/tools/dashboard_generator.py +2638 -0
  266. package/tools/db_sandbox.py +198 -20
  267. package/tools/e2e_flow_prober.ps1 +12 -64
  268. package/tools/e2e_flow_prober.py +99 -0
  269. package/tools/e2e_flow_prober.sh +4 -0
  270. package/tools/flaky_detector.py +4 -3
  271. package/tools/generate_allure_report.ps1 +12 -29
  272. package/tools/generate_allure_report.py +77 -0
  273. package/tools/generate_allure_report.sh +4 -0
  274. package/tools/generate_jmx_plan.py +257 -58
  275. package/tools/generate_test_reports.ps1 +12 -134
  276. package/tools/generate_test_reports.py +133 -0
  277. package/tools/generate_test_reports.sh +4 -0
  278. package/tools/gherkin_suite.py +758 -0
  279. package/tools/git_pre_commit_guard.py +12 -6
  280. package/tools/goldfish_review.py +366 -0
  281. package/tools/heal_pr_automator.py +1 -1
  282. package/tools/heuristic_locator_matcher.py +170 -21
  283. package/tools/infer_linter.py +612 -70
  284. package/tools/lint_ai_docs.py +64 -0
  285. package/tools/matrix_sharder.py +1 -1
  286. package/tools/mcp_excel_server.ps1 +12 -250
  287. package/tools/mcp_excel_server.py +249 -0
  288. package/tools/mcp_excel_server.sh +4 -0
  289. package/tools/mcp_server.py +368 -144
  290. package/tools/md_to_xlsx.py +307 -0
  291. package/tools/micro_mutator.py +300 -69
  292. package/tools/omni_transpiler.py +2 -2
  293. package/tools/output_formatter.py +128 -0
  294. package/tools/pinpoint_failure.py +88 -88
  295. package/tools/project_state.py +2 -2
  296. package/tools/repo_map.py +1 -1
  297. package/tools/safety_firewall.py +196 -45
  298. package/tools/setup_project.py +294 -30
  299. package/tools/spec_drift_sentinel.py +439 -0
  300. package/tools/sync_agent_rules.py +248 -0
  301. package/tools/telemetry_correlator.py +45 -0
  302. package/tools/test_impact.py +51 -12
  303. package/tools/tms_sync_bridge.py +21 -5
  304. package/tools/universal_ir.py +3 -0
  305. package/tools/update_sentinel.py +87 -15
  306. package/tools/verify_all.ps1 +13 -0
  307. package/tools/verify_all.py +463 -79
  308. package/tools/visual_diff_canvas.py +1 -0
  309. package/tools/visual_som_helper.py +1 -1
  310. package/tools/watch_mode.py +305 -0
  311. package/tools/welcome_page.py +291 -0
@@ -17,7 +17,7 @@ Verifies:
17
17
  Gate 5: Cleanup Blacklist: No temporary properties or debug dump files on disk.
18
18
  Gate 6: Credential Security: No plaintext passwords in Profiles/default.glbl or repository.
19
19
  Gate 7: Static Defect Sentinel: Meta Infer-style AST scan for fake passes and empty catches.
20
- Gate 8: AST Micro-Mutation Sentinel: Injects syntactic faults to eliminate tautological tests (Mutation Score >= 80%).
20
+ Gate 8: Syntactic Micro-Mutation Sentinel: Injects syntactic faults to eliminate tautological tests (Mutation Score >= 80%).
21
21
 
22
22
  Supports:
23
23
  - Hub Mode: Verifies the centralized QA Hub / skills repository integrity.
@@ -32,6 +32,11 @@ import sys
32
32
  import subprocess
33
33
  import glob
34
34
  import re
35
+ import json
36
+ import io
37
+ from datetime import datetime
38
+ import ast
39
+ import builtins
35
40
  import argparse
36
41
 
37
42
  # Enforce UTF-8 stdout encoding for Windows console compatibility
@@ -130,7 +135,7 @@ def resolve_target_repo(cli_target=None, cli_repo=None):
130
135
 
131
136
 
132
137
  def gate_0_author_integrity(repo_root, mode="AUTO"):
133
- print("\n[GATE 0] Running Author Attribution & Anti-Tamper Integrity Guard...")
138
+ print("\n[GATE 0] Verifying Author Attribution & MIT License Integrity...")
134
139
  try:
135
140
  from author_integrity import (
136
141
  verify_author_attribution,
@@ -148,47 +153,89 @@ def gate_0_author_integrity(repo_root, mode="AUTO"):
148
153
  DEFAULT_REPO_ROOT,
149
154
  )
150
155
 
151
- # 1. Cryptographic SHA-256 fingerprint verification
156
+ # 1. Author metadata hash verification
152
157
  if not verify_author_fingerprint():
153
- print("[FAIL] Cryptographic author fingerprint mismatch! Original creator ('Thế Vị @TeeVee998') has been tampered with.")
158
+ print("[FAIL] Author attribution reference mismatch! Original creator ('Thế Vị @TeeVee998') has been altered.")
154
159
  return False
155
160
 
156
161
  # 2. In HUB mode, verify the Hub repository itself.
157
162
  # In TARGET mode, verify the Hub tools root to guarantee engine integrity.
158
163
  check_root = repo_root if mode == "HUB" else DEFAULT_REPO_ROOT
159
164
  valid, violations = verify_author_attribution(check_root)
160
- if not valid:
161
- print("[FAIL] Author Attribution & Copyright Integrity Violated:")
162
- for v in violations:
165
+ license_violations = [v for v in violations if "LICENSE" in v]
166
+ if license_violations:
167
+ print("[FAIL] Author Attribution & MIT License Violated:")
168
+ for v in license_violations:
163
169
  print(f" • {v}")
164
- print(" Under MIT License, original author attribution to 'Thế Vị (@TeeVee998)' must be preserved.")
170
+ print(" Under MIT License, original author copyright notice to 'Thế Vị (@TeeVee998)' must be preserved in LICENSE.")
165
171
  return False
166
- print(f"[PASS] Author Attribution & Cryptographic Provenance verified intact: {get_canonical_watermark()}")
172
+ elif not valid:
173
+ # Informational notices for downstream forks with customized package.json / README
174
+ print("[INFO] Downstream attribution notice:")
175
+ for v in violations:
176
+ print(f" • {v}")
177
+ print(f"[PASS] Author Attribution & MIT License Provenance verified intact: {get_canonical_watermark()}")
167
178
  return True
168
179
 
169
180
 
181
+ MOJIBAKE_INDICATORS = (
182
+ "\u00c3\u00a1", # á
183
+ "\u00c3\u00a3", # ã
184
+ "\u00c3\u00a9", # é
185
+ "\u00c3\u00a8", # è
186
+ "\u00c3\u00b3", # ó
187
+ "\u00c3\u00b2", # ò
188
+ "\ufffd", # replacement char
189
+ "\u00e2\u20ac\u2014", # double encoded em-dash
190
+ "\u00e2\u20ac\u0153", # double encoded open quote
191
+ "\u00e2\u20ac\u009d", # double encoded close quote
192
+ )
193
+
194
+
170
195
  def gate_1_bom_check(repo_root, mode="AUTO"):
171
- print("\n[GATE 1] Scanning UTF-8 No BOM across repository...")
172
- violations = []
196
+ print("\n[GATE 1] Scanning UTF-8 No BOM and Encoding Integrity across repository...")
197
+ ignored_dirs = {".git", ".gradle", "node_modules", "allure-results", "graphify-out", "__pycache__", "build", "dist", "claude-ecosystem-repos"}
198
+ ignored_files = {"verify_all.py", "test_tools.py"}
199
+ bom_violations = []
200
+ mojibake_violations = []
173
201
  for root, dirs, files in os.walk(repo_root):
174
- if any(ignored in root for ignored in [".git", ".gradle", "node_modules", "allure-results", "graphify-out"]):
175
- continue
202
+ dirs[:] = [d for d in dirs if d not in ignored_dirs]
176
203
  for f in files:
177
204
  if f.endswith(BOM_EXTENSIONS):
178
205
  full_path = os.path.join(root, f)
206
+ rel_path = os.path.relpath(full_path, repo_root)
207
+ if f in ignored_files:
208
+ continue
179
209
  try:
180
210
  with open(full_path, "rb") as fp:
181
- if fp.read(3) == b"\xef\xbb\xbf":
182
- violations.append(os.path.relpath(full_path, repo_root))
211
+ raw = fp.read()
212
+ if raw[:3] == b"\xef\xbb\xbf":
213
+ bom_violations.append(rel_path)
214
+ try:
215
+ text = raw.decode("utf-8")
216
+ if any(ind in text for ind in MOJIBAKE_INDICATORS):
217
+ mojibake_violations.append(rel_path)
218
+ except UnicodeDecodeError:
219
+ mojibake_violations.append(f"{rel_path} (Invalid UTF-8 decode)")
183
220
  except Exception:
184
221
  pass
185
222
 
186
- if violations:
187
- print(f"[FAIL] {len(violations)} files contain UTF-8 BOM bytes:")
188
- for v in violations:
223
+ failed = False
224
+ if bom_violations:
225
+ print(f"[FAIL] {len(bom_violations)} files contain UTF-8 BOM bytes:")
226
+ for v in bom_violations:
227
+ print(f" - {v}")
228
+ failed = True
229
+ if mojibake_violations:
230
+ print(f"[FAIL] {len(mojibake_violations)} files contain Mojibake / Corrupted Encoding:")
231
+ for v in mojibake_violations:
189
232
  print(f" - {v}")
233
+ failed = True
234
+
235
+ if failed:
190
236
  return False
191
- print("[PASS] 100% UTF-8 No BOM verified across all source files.")
237
+
238
+ print("[PASS] 100% UTF-8 No BOM & Clean Encoding verified across all source files.")
192
239
  return True
193
240
 
194
241
 
@@ -197,11 +244,12 @@ def gate_2_path_portability(repo_root, mode="AUTO"):
197
244
  ignored_dirs = {
198
245
  ".git", ".gradle", "node_modules", "allure-results", "Screenshots",
199
246
  "Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings", "graphify-out",
200
- "__pycache__", "agent_qa.egg-info", "dist", "build"
247
+ "__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos"
201
248
  }
202
249
  ignored_files = {
203
250
  ".project", ".classpath", ".DS_Store", "memory_cache.db",
204
- "verify_all.py", "git_pre_commit_guard.py", "agent_canary.py", "repo_map.txt", "repo_map.json"
251
+ "verify_all.py", "git_pre_commit_guard.py", "agent_canary.py", "repo_map.txt", "repo_map.json",
252
+ "PROJECT_HEALTH_REPORT.md", "PROJECT_STATE.md"
205
253
  }
206
254
  violations = []
207
255
  for root, dirs, files in os.walk(repo_root):
@@ -240,7 +288,7 @@ def gate_3_clean_pom(repo_root, mode="AUTO"):
240
288
  print("\n[GATE 3] Checking Clean POM & Flat Architecture in Test Scripts...")
241
289
  effective_mode = detect_repo_mode(repo_root) if mode == "AUTO" else mode
242
290
  if effective_mode == "HUB":
243
- print("[PASS] Hub Mode: Repository contains QA Hub assets; Clean POM applies to target test suites.")
291
+ print("[N/A] Hub Mode: Not Applicable (Clean POM rules apply to target test suites in Spokes).")
244
292
  return True
245
293
 
246
294
  test_dirs = ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
@@ -252,9 +300,14 @@ def gate_3_clean_pom(repo_root, mode="AUTO"):
252
300
  test_files.extend(glob.glob(os.path.join(d, "**", ext), recursive=True))
253
301
 
254
302
  if not test_files:
255
- print("[SKIP] No test scripts present in candidate directories (Scripts/, tests/, e2e/).")
303
+ print("[N/A] No test scripts present in candidate directories (Scripts/, tests/, e2e/).")
256
304
  return True
257
305
 
306
+ LOCATOR_CALL_PATTERN = re.compile(
307
+ r'(?:\.(?:click|dblclick|fill|type|press|check|locator|waitForSelector|hover|selectOption)|cy\.(?:get|find)|driver\.find[A-Za-z]*|WebUI\.[a-zA-Z]+)\s*\(\s*[\x22\x27](?P<sel>[^\x22\x27]+)[\x22\x27]'
308
+ )
309
+ RAW_XPATH_PATTERN = re.compile(r'[\x22\x27](?:xpath=|\/{2}[a-zA-Z*@\[])[^\x22\x27]*[\x22\x27]')
310
+
258
311
  violations = []
259
312
  for full_path in sorted(set(test_files)):
260
313
  rel_path = os.path.relpath(full_path, repo_root)
@@ -264,10 +317,21 @@ def gate_3_clean_pom(repo_root, mode="AUTO"):
264
317
  stripped = line.strip()
265
318
  if stripped.startswith("//") or stripped.startswith("/*") or stripped.startswith("#"):
266
319
  continue
320
+ # Ignore standard navigation statements to prevent false-positive XPath violations on URLs
321
+ if re.search(r'\.(?:goto|get|open|navigate)\s*\(\s*[\x22\x27]https?:\/\/', stripped):
322
+ continue
267
323
  if "findTestObject" in line:
268
324
  violations.append((rel_path, line_no, "Raw findTestObject"))
269
- if "//" in line and ("//table" in line or "//input" in line or "//button" in line):
270
- violations.append((rel_path, line_no, "Raw XPath"))
325
+ continue
326
+ # Check locator calls taking raw CSS/XPath selectors
327
+ m = LOCATOR_CALL_PATTERN.search(stripped)
328
+ if m:
329
+ sel = m.group('sel').strip()
330
+ if any(sel.startswith(p) for p in ('.', '#', '//', 'xpath=', '[')) or ' > ' in sel:
331
+ violations.append((rel_path, line_no, f"Raw CSS/XPath Selector in Test Script: '{sel}'"))
332
+ continue
333
+ if RAW_XPATH_PATTERN.search(stripped):
334
+ violations.append((rel_path, line_no, "Raw XPath literal in Test Script"))
271
335
  except Exception:
272
336
  pass
273
337
 
@@ -289,7 +353,11 @@ def gate_4_ai_docs_lint(repo_root, mode="AUTO"):
289
353
  print("[FAIL] lint_ai_docs.py not found in tools/!")
290
354
  return False
291
355
 
292
- res = subprocess.run([sys.executable, linter_script, repo_root], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
356
+ try:
357
+ res = subprocess.run([sys.executable, linter_script, repo_root], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=60.0)
358
+ except subprocess.TimeoutExpired:
359
+ print("[FAIL] lint_ai_docs.py timed out after 60s!")
360
+ return False
293
361
  if res.returncode != 0:
294
362
  print(res.stdout)
295
363
  print(res.stderr)
@@ -352,7 +420,11 @@ def gate_6_credential_security(repo_root, mode="AUTO"):
352
420
  # 2. Universal Repository Scan for Hardcoded Plaintext Secrets
353
421
  secret_patterns = [
354
422
  (re.compile(r'\bghp_[0-9a-zA-Z]{36}\b'), "GitHub Personal Access Token"),
423
+ (re.compile(r'\bgithub_pat_[0-9a-zA-Z_]{82}\b'), "GitHub Fine-Grained Personal Access Token"),
355
424
  (re.compile(r'\bAKIA[0-9A-Z]{16}\b'), "AWS Access Key ID"),
425
+ (re.compile(r'\bsk_(?:live|test)_[0-9a-zA-Z]{24,}\b'), "Stripe Secret Key"),
426
+ (re.compile(r'\bxox[baprs]-[0-9a-zA-Z-]{10,}\b'), "Slack API Token"),
427
+ (re.compile(r'\bAIza[0-9A-Za-z-_]{35}\b'), "Google API Key"),
356
428
  (re.compile(r'-----BEGIN (?:RSA )?PRIVATE KEY-----'), "Private Key Header"),
357
429
  (re.compile(r'(?i)\b(?:Pass(?:word)?@?\d+|Admin@?\d+|Root@?\d+|Default@?\d+)\b'), "Known Default Password"), # gate6-allowlist
358
430
  (re.compile(r'(?i)\b(?:password|passwd|pwd)\s*[:=]\s*[\x22\x27][^\x22\x27]{4,}[\x22\x27]'), "Hardcoded Plaintext Password Assignment"), # gate6-allowlist
@@ -362,14 +434,14 @@ def gate_6_credential_security(repo_root, mode="AUTO"):
362
434
  "System.getenv", "os.getenv", "os.environ", "process.env",
363
435
  "dummy", "placeholder", "***", "<password>", "your_password",
364
436
  "test_password", "mock_", "NEO4J_PASSWORD", "DynamicTestPwd",
365
- "secret_patterns", "PASSWORD", 'password = ""', "password = ''",
437
+ "secret_patterns", "<name>PASSWORD</name>", 'password = ""', "password = ''",
366
438
  "type=str", "help=", "# gate6-allowlist", "# nosec"
367
439
  ]
368
440
 
369
441
  ignored_dirs = {
370
442
  ".git", ".gradle", "node_modules", "allure-results", "Screenshots",
371
443
  "Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings", "graphify-out",
372
- "__pycache__", "agent_qa.egg-info", "dist", "build"
444
+ "__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos"
373
445
  }
374
446
 
375
447
  for root, dirs, files in os.walk(repo_root):
@@ -401,12 +473,221 @@ def gate_6_credential_security(repo_root, mode="AUTO"):
401
473
  return True
402
474
 
403
475
 
476
+ def check_python_ast_integrity(repo_root):
477
+ """
478
+ Performs static AST validation across all Python files in the repository.
479
+ Enforces F821 parity (zero undefined variable names) and zero syntax errors.
480
+ """
481
+ builtin_names = set(dir(builtins)) | {'__file__', '__name__', '__doc__', '__package__'}
482
+ py_files = []
483
+ ignored = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'claude-ecosystem-repos', 'build', 'dist', '.gradle'}
484
+
485
+ for root, dirs, files in os.walk(repo_root):
486
+ dirs[:] = [d for d in dirs if d not in ignored]
487
+ for f in files:
488
+ if f.endswith('.py'):
489
+ py_files.append(os.path.join(root, f))
490
+
491
+ violations = []
492
+
493
+ for file_path in sorted(py_files):
494
+ rel_path = os.path.relpath(file_path, repo_root)
495
+ try:
496
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as fp:
497
+ content = fp.read()
498
+ tree = ast.parse(content, file_path)
499
+ except SyntaxError as e:
500
+ violations.append((rel_path, e.lineno or 1, f"SyntaxError: {e.msg}"))
501
+ continue
502
+
503
+ module_names = set(builtin_names)
504
+ for stmt in tree.body:
505
+ if isinstance(stmt, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
506
+ module_names.add(stmt.name)
507
+ elif isinstance(stmt, ast.Import):
508
+ for a in stmt.names:
509
+ module_names.add(a.asname or a.name.split('.')[0])
510
+ elif isinstance(stmt, ast.ImportFrom):
511
+ for a in stmt.names:
512
+ module_names.add(a.asname or a.name)
513
+ elif isinstance(stmt, (ast.Assign, ast.AnnAssign)):
514
+ targets = [stmt.target] if isinstance(stmt, ast.AnnAssign) else stmt.targets
515
+ for t in targets:
516
+ for n in ast.walk(t):
517
+ if isinstance(n, ast.Name):
518
+ module_names.add(n.id)
519
+ for n in ast.walk(stmt):
520
+ if hasattr(ast, 'NamedExpr') and isinstance(n, ast.NamedExpr) and isinstance(getattr(n, 'target', None), ast.Name):
521
+ module_names.add(n.target.id)
522
+
523
+ class ScopeVisitor(ast.NodeVisitor):
524
+ def __init__(self):
525
+ self.scopes = [set(module_names)]
526
+ self.file_violations = []
527
+
528
+ def visit_FunctionDef(self, node):
529
+ self.scopes[-1].add(node.name)
530
+ fn_scope = set(self.scopes[-1])
531
+ for arg in node.args.posonlyargs + node.args.args + node.args.kwonlyargs:
532
+ fn_scope.add(arg.arg)
533
+ if node.args.vararg:
534
+ fn_scope.add(node.args.vararg.arg)
535
+ if node.args.kwarg:
536
+ fn_scope.add(node.args.kwarg.arg)
537
+ self.scopes.append(fn_scope)
538
+ self.generic_visit(node)
539
+ self.scopes.pop()
540
+
541
+ visit_AsyncFunctionDef = visit_FunctionDef
542
+
543
+ def visit_ClassDef(self, node):
544
+ self.scopes[-1].add(node.name)
545
+ cls_scope = set(self.scopes[-1])
546
+ self.scopes.append(cls_scope)
547
+ self.generic_visit(node)
548
+ self.scopes.pop()
549
+
550
+ def visit_Assign(self, node):
551
+ for t in node.targets:
552
+ for n in ast.walk(t):
553
+ if isinstance(n, ast.Name):
554
+ self.scopes[-1].add(n.id)
555
+ self.generic_visit(node)
556
+
557
+ def visit_AnnAssign(self, node):
558
+ if isinstance(node.target, ast.Name):
559
+ self.scopes[-1].add(node.target.id)
560
+ self.generic_visit(node)
561
+
562
+ def visit_For(self, node):
563
+ for n in ast.walk(node.target):
564
+ if isinstance(n, ast.Name):
565
+ self.scopes[-1].add(n.id)
566
+ self.generic_visit(node)
567
+
568
+ def visit_With(self, node):
569
+ for item in node.items:
570
+ if item.optional_vars:
571
+ for n in ast.walk(item.optional_vars):
572
+ if isinstance(n, ast.Name):
573
+ self.scopes[-1].add(n.id)
574
+ self.generic_visit(node)
575
+
576
+ def visit_ExceptHandler(self, node):
577
+ if node.name:
578
+ self.scopes[-1].add(node.name)
579
+ self.generic_visit(node)
580
+
581
+ def visit_NamedExpr(self, node):
582
+ if isinstance(node.target, ast.Name):
583
+ for s in self.scopes:
584
+ s.add(node.target.id)
585
+ self.generic_visit(node)
586
+
587
+ def visit_Match(self, node):
588
+ for case in getattr(node, 'cases', []):
589
+ pattern = getattr(case, 'pattern', None)
590
+ if pattern:
591
+ for n in ast.walk(pattern):
592
+ name = getattr(n, 'name', None)
593
+ if isinstance(name, str):
594
+ self.scopes[-1].add(name)
595
+ rest = getattr(n, 'rest', None)
596
+ if isinstance(rest, str):
597
+ self.scopes[-1].add(rest)
598
+ self.generic_visit(node)
599
+
600
+ def visit_Global(self, node):
601
+ for name in node.names:
602
+ for s in self.scopes:
603
+ s.add(name)
604
+ self.generic_visit(node)
605
+
606
+ def visit_Nonlocal(self, node):
607
+ for name in node.names:
608
+ self.scopes[-1].add(name)
609
+ self.generic_visit(node)
610
+
611
+ def visit_Import(self, node):
612
+ for a in node.names:
613
+ self.scopes[-1].add(a.asname or a.name.split('.')[0])
614
+
615
+ def visit_ImportFrom(self, node):
616
+ for a in node.names:
617
+ self.scopes[-1].add(a.asname or a.name)
618
+
619
+ def visit_ListComp(self, node):
620
+ comp_scope = set(self.scopes[-1])
621
+ for gen in node.generators:
622
+ for n in ast.walk(gen.target):
623
+ if isinstance(n, ast.Name):
624
+ comp_scope.add(n.id)
625
+ self.scopes.append(comp_scope)
626
+ self.generic_visit(node)
627
+ self.scopes.pop()
628
+
629
+ visit_SetComp = visit_ListComp
630
+ visit_GeneratorExp = visit_ListComp
631
+ visit_DictComp = visit_ListComp
632
+
633
+ def visit_Lambda(self, node):
634
+ lam_scope = set(self.scopes[-1])
635
+ for arg in node.args.posonlyargs + node.args.args + node.args.kwonlyargs:
636
+ lam_scope.add(arg.arg)
637
+ if node.args.vararg:
638
+ lam_scope.add(node.args.vararg.arg)
639
+ if node.args.kwarg:
640
+ lam_scope.add(node.args.kwarg.arg)
641
+ self.scopes.append(lam_scope)
642
+ self.generic_visit(node)
643
+ self.scopes.pop()
644
+
645
+ def visit_Name(self, node):
646
+ if isinstance(node.ctx, ast.Load):
647
+ if node.id.startswith('__') and node.id.endswith('__'):
648
+ return
649
+ if not any(node.id in s for s in reversed(self.scopes)):
650
+ self.file_violations.append((rel_path, node.lineno, f"Undefined name '{node.id}' (F821)"))
651
+
652
+ visitor = ScopeVisitor()
653
+ visitor.visit(tree)
654
+ violations.extend(visitor.file_violations)
655
+
656
+ return violations
657
+
658
+
404
659
  def gate_7_infer_linter(repo_root, mode="AUTO"):
405
660
  print("\n[GATE 7] Running Meta Infer-Style Static Defect & Fake-Pass Sentinel...")
406
661
  effective_mode = detect_repo_mode(repo_root) if mode == "AUTO" else mode
662
+
663
+ # Static Python AST & Undefined Name (F821) Sentinel
664
+ ast_violations = check_python_ast_integrity(repo_root)
665
+ if ast_violations:
666
+ print(f"[FAIL] Static Python AST Sentinel detected {len(ast_violations)} syntax errors or undefined names:")
667
+ for rel_file, line_no, desc in ast_violations:
668
+ print(f" - {rel_file}:{line_no} -> {desc}")
669
+ return False
670
+ print("[PASS] 100% Python AST integrity verified (zero syntax errors, zero undefined variables).")
671
+
672
+ # In HUB mode: dogfood tools/ with baseline to prevent new unbaselined defects
407
673
  if effective_mode == "HUB":
408
- print("[PASS] Hub Mode: Static Defect Sentinel active for target projects.")
409
- return True
674
+ baseline_file = os.path.join(repo_root, "tools", ".infer_baseline.json")
675
+ infer_script = os.path.join(repo_root, "tools", "infer_linter.py")
676
+ if os.path.exists(baseline_file) and os.path.exists(infer_script):
677
+ tools_dir = os.path.join(repo_root, "tools")
678
+ try:
679
+ res = subprocess.run([
680
+ sys.executable, infer_script, "--strict", "--baseline", baseline_file, "--dir", tools_dir
681
+ ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=60.0)
682
+ if res.returncode != 0:
683
+ print("[FAIL] Hub internal tools contain unbaselined critical defects:")
684
+ print(res.stdout)
685
+ return False
686
+ except subprocess.TimeoutExpired:
687
+ print("[FAIL] infer_linter.py timed out after 60s!")
688
+ return False
689
+ except (OSError, subprocess.SubprocessError):
690
+ pass
410
691
 
411
692
  test_dirs = ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
412
693
  has_tests = False
@@ -423,7 +704,7 @@ def gate_7_infer_linter(repo_root, mode="AUTO"):
423
704
  break
424
705
 
425
706
  if not has_tests:
426
- print("[SKIP] No test scripts (Groovy/TS/JS/Py) present in candidate test directories.")
707
+ print("[N/A] No test scripts (Groovy/TS/JS/Py) present in candidate test directories.")
427
708
  return True
428
709
 
429
710
  infer_script = os.path.join(SCRIPT_DIR, "infer_linter.py")
@@ -431,17 +712,22 @@ def gate_7_infer_linter(repo_root, mode="AUTO"):
431
712
  infer_script = os.path.join(repo_root, "tools", "infer_linter.py")
432
713
 
433
714
  if os.path.exists(infer_script):
434
- res = subprocess.run([sys.executable, infer_script, "--dir", target_scan_dir], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
435
- if res.returncode != 0:
436
- print("[FAIL] Static Defect Sentinel detected critical issues in test scripts:")
437
- print(res.stdout)
715
+ try:
716
+ res = subprocess.run([sys.executable, infer_script, "--strict", "--dir", target_scan_dir], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=60.0)
717
+ if res.returncode != 0:
718
+ print("[FAIL] Static Defect Sentinel detected critical issues in test scripts:")
719
+ print(res.stdout)
720
+ return False
721
+ except subprocess.TimeoutExpired:
722
+ print("[FAIL] infer_linter.py timed out after 60s!")
438
723
  return False
439
- print("[PASS] Zero critical defects / zero swallowed exceptions across test scripts.")
724
+ print("[PASS] Zero critical defects / zero swallowed exceptions across active test suites.")
440
725
  return True
441
726
 
442
727
 
443
- def gate_8_micro_mutator(repo_root, mode="AUTO"):
444
- print("\n[GATE 8] Running AST Micro-Mutation Testing Sentinel (Min Mutation Score >= 80%)...")
728
+ def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
729
+ eval_mode_desc = f"DYNAMIC EXECUTION MODE ({test_cmd})" if test_cmd else "HEURISTIC STATIC MODE"
730
+ print(f"\n[GATE 8] Running Assertion Sensitivity & Syntactic Mutation Sentinel [{eval_mode_desc}] (Min Score >= 80%)...")
445
731
  scripts_dir = os.path.join(repo_root, "Scripts")
446
732
  tests_dir = os.path.join(repo_root, "tests")
447
733
 
@@ -450,17 +736,17 @@ def gate_8_micro_mutator(repo_root, mode="AUTO"):
450
736
  groovy_files = [f for f in glob.glob(os.path.join(scripts_dir, "**", "*.groovy"), recursive=True)]
451
737
  if groovy_files:
452
738
  target_dir = scripts_dir
453
- elif os.path.isdir(tests_dir):
739
+ if not target_dir and os.path.isdir(tests_dir):
454
740
  test_files = [f for f in glob.glob(os.path.join(tests_dir, "**", "*.*"), recursive=True) if f.endswith((".py", ".ts", ".js", ".groovy"))]
455
741
  if test_files:
456
742
  target_dir = tests_dir
457
743
 
458
744
  if not target_dir:
459
745
  if mode == "HUB":
460
- print("[PASS] Hub Mode: AST Micro-Mutation engine verified and operational.")
746
+ print(f"[N/A] Hub Mode: Not Applicable (Assertion Sensitivity & Syntactic Mutation applies to target test suites in Spokes) [{eval_mode_desc}].")
461
747
  return True
462
748
  else:
463
- print("[SKIP] No automated test scripts found in Scripts/ or tests/ to mutate.")
749
+ print("[N/A] No automated test scripts found in Scripts/ or tests/ to mutate.")
464
750
  return True
465
751
 
466
752
  mutator_script = os.path.join(SCRIPT_DIR, "micro_mutator.py")
@@ -468,13 +754,20 @@ def gate_8_micro_mutator(repo_root, mode="AUTO"):
468
754
  mutator_script = os.path.join(repo_root, "tools", "micro_mutator.py")
469
755
 
470
756
  if os.path.exists(mutator_script):
471
- res = subprocess.run([sys.executable, mutator_script, target_dir, "--min-score", "80.0", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
757
+ cmd = [sys.executable, mutator_script, target_dir, "--min-score", "80.0", "--json"]
758
+ if test_cmd:
759
+ cmd.extend(["--test-cmd", test_cmd])
760
+ try:
761
+ res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=120.0)
762
+ except subprocess.TimeoutExpired:
763
+ print("[FAIL] Assertion Sensitivity Sentinel timed out after 120s!")
764
+ return False
472
765
  if res.returncode != 0:
473
- print("[FAIL] AST Micro-Mutation Sentinel REJECTED: Mutation score < 80% or unasserted logic found.")
766
+ print("[FAIL] Assertion Sensitivity Sentinel REJECTED: Score < 80% or unasserted logic found.")
474
767
  try:
475
768
  data = json.loads(res.stdout)
476
- print(f" Mutation Score: {data.get('mutation_score', 0)}% (Required: >= 80%)")
477
- print(f" Mutants Killed: {data.get('killed', 0)} / {data.get('total_mutants', 0)}")
769
+ print(f" Assertion Sensitivity: {data.get('mutation_score', 0)}% (Required: >= 80%)")
770
+ print(f" Mutants Caught: {data.get('killed', 0)} / {data.get('total_mutants', 0)}")
478
771
  except Exception:
479
772
  if res.stdout:
480
773
  print(res.stdout)
@@ -482,54 +775,121 @@ def gate_8_micro_mutator(repo_root, mode="AUTO"):
482
775
  else:
483
776
  try:
484
777
  data = json.loads(res.stdout)
485
- print(f"[PASS] Mutation Score: {data.get('mutation_score', 100)}% >= 80% (Killed {data.get('killed', 0)}/{data.get('total_mutants', 0)} mutants). Zero Tautological Tests.")
778
+ mode_label = "DYNAMIC" if test_cmd else "HEURISTIC STATIC"
779
+ print(f"[PASS] Assertion Sensitivity [{mode_label}]: {data.get('mutation_score', 100)}% >= 80% (Caught {data.get('killed', 0)}/{data.get('total_mutants', 0)} syntactic mutants). Zero unasserted logic branches.")
486
780
  except Exception:
487
- print("[PASS] AST Micro-Mutation Sentinel passed: Mutation score >= 80%.")
781
+ print(f"[PASS] Assertion Sensitivity Sentinel passed: Score >= 80% [{eval_mode_desc}].")
488
782
  return True
489
783
 
490
- print("[PASS] AST Micro-Mutation verified.")
784
+ print("[PASS] Assertion Sensitivity verified.")
491
785
  return True
492
786
 
493
787
 
494
- def main():
495
- parser = argparse.ArgumentParser(description="Enterprise DoD Verification Sentinel")
496
- parser.add_argument("target", nargs="?", default=None,
497
- help="Target repository path to verify (defaults to auto-detection from CWD or Hub)")
498
- parser.add_argument("--repo", "--path", dest="repo_path", default=None,
499
- help="Target repository path to verify")
500
- parser.add_argument("--mode", choices=["auto", "hub", "target"], default="auto",
501
- help="Execution mode (auto-detect, hub, or target test project)")
502
- args = parser.parse_args()
788
+ def load_synapse_config(repo_root=None, config_path=None):
789
+ """Loads configuration from synapse.config.json if present."""
790
+ if config_path and os.path.exists(config_path):
791
+ try:
792
+ with open(config_path, "r", encoding="utf-8") as f:
793
+ return json.load(f)
794
+ except (OSError, json.JSONDecodeError):
795
+ pass
796
+ root = repo_root or REPO_ROOT
797
+ cand = os.path.join(root, "synapse.config.json")
798
+ if os.path.exists(cand):
799
+ try:
800
+ with open(cand, "r", encoding="utf-8") as f:
801
+ return json.load(f)
802
+ except (OSError, json.JSONDecodeError):
803
+ pass
804
+ return {}
503
805
 
504
- try:
505
- repo_root = resolve_target_repo(cli_target=args.target, cli_repo=args.repo_path)
506
- except FileNotFoundError as e:
507
- print(f"[ERROR] {e}")
508
- return 1
509
806
 
510
- effective_mode = detect_repo_mode(repo_root) if args.mode == "auto" else args.mode.upper()
807
+ def format_gates_junit(gate_results, repo_root):
808
+ """Formats 9 DoD Quality Gates result into JUnit XML testsuite."""
809
+ total = len(gate_results)
810
+ failures = len([g for g in gate_results if not g[1]])
811
+ lines = [
812
+ '<?xml version="1.0" encoding="utf-8"?>',
813
+ f'<testsuite name="synapse-quality-gates" tests="{total}" failures="{failures}" errors="0">'
814
+ ]
815
+ for name, success, reason in gate_results:
816
+ cname = "synapse.quality_gates"
817
+ tname = name.split(":")[0].strip().replace(" ", "_")
818
+ lines.append(f' <testcase classname="{cname}" name="{tname}">')
819
+ if not success:
820
+ lines.append(f' <failure message="Quality Gate Failed: {name}">{name} failed in repository {repo_root} ({reason})</failure>')
821
+ lines.append(' </testcase>')
822
+ lines.append('</testsuite>')
823
+ return "\n".join(lines)
824
+
511
825
 
512
- print_header(f"ENTERPRISE AUTOMATION - ONE-COMMAND DoD VERIFICATION [{effective_mode} MODE]")
513
- print(f"Target Repository: {repo_root}")
514
- print(f"Operational Mode : {effective_mode}")
826
+ def run_all_gates(repo_root, mode="AUTO", test_cmd="", output_format="text", config_path=None):
827
+ """Programmatic entrypoint to execute all 9 DoD Quality Gates with config and format support."""
828
+ effective_mode = detect_repo_mode(repo_root) if mode.upper() == "AUTO" else mode.upper()
829
+ cfg = load_synapse_config(repo_root, config_path=config_path)
830
+ qg_cfg = cfg.get("quality_gates", {})
831
+ gate_toggles = qg_cfg.get("gates", {})
832
+
833
+ if output_format == "text":
834
+ print_header(f"ENTERPRISE AUTOMATION - ONE-COMMAND DoD VERIFICATION [{effective_mode} MODE]")
835
+ print(f"Target Repository: {repo_root}")
836
+ print(f"Operational Mode : {effective_mode}")
515
837
 
516
838
  gates = [
517
- ("Gate 0: Author Attribution & Integrity", gate_0_author_integrity),
518
- ("Gate 1: UTF-8 No BOM", gate_1_bom_check),
519
- ("Gate 2: Path Portability", gate_2_path_portability),
520
- ("Gate 3: Clean POM & Flat Architecture", gate_3_clean_pom),
521
- ("Gate 4: AI Documentation Linter", gate_4_ai_docs_lint),
522
- ("Gate 5: Temporary Files Blacklist", gate_5_cleanup_blacklist),
523
- ("Gate 6: Credential Security", gate_6_credential_security),
524
- ("Gate 7: Static Defect Sentinel", gate_7_infer_linter),
525
- ("Gate 8: AST Micro-Mutation Sentinel", gate_8_micro_mutator),
839
+ ("gate_0_author_integrity", "Gate 0: Author Attribution & Integrity", gate_0_author_integrity),
840
+ ("gate_1_bom_check", "Gate 1: UTF-8 No BOM", gate_1_bom_check),
841
+ ("gate_2_path_portability", "Gate 2: Path Portability", gate_2_path_portability),
842
+ ("gate_3_clean_pom", "Gate 3: Clean POM & Flat Architecture", gate_3_clean_pom),
843
+ ("gate_4_ai_docs_lint", "Gate 4: AI Documentation Linter", gate_4_ai_docs_lint),
844
+ ("gate_5_cleanup_blacklist", "Gate 5: Temporary Files Blacklist", gate_5_cleanup_blacklist),
845
+ ("gate_6_credential_security", "Gate 6: Credential Security", gate_6_credential_security),
846
+ ("gate_7_infer_linter", "Gate 7: Static Defect Sentinel", gate_7_infer_linter),
847
+ ("gate_8_micro_mutator", "Gate 8: Assertion Sensitivity & Syntactic Mutation Sentinel", lambda r, mode: gate_8_micro_mutator(r, mode=mode, test_cmd=test_cmd)),
526
848
  ]
527
849
 
528
850
  all_passed = True
529
- for name, gate_fn in gates:
530
- success = gate_fn(repo_root, mode=effective_mode)
531
- if not success:
532
- all_passed = False
851
+ gate_results = []
852
+
853
+ if output_format in ("json", "junit"):
854
+ old_stdout = sys.stdout
855
+ sys.stdout = io.StringIO()
856
+ try:
857
+ for g_key, name, gate_fn in gates:
858
+ if gate_toggles.get(g_key) is False:
859
+ gate_results.append((name, True, "SKIPPED_BY_CONFIG"))
860
+ continue
861
+ success = gate_fn(repo_root, mode=effective_mode)
862
+ gate_results.append((name, success, "PASS" if success else "FAIL"))
863
+ if not success:
864
+ all_passed = False
865
+ finally:
866
+ sys.stdout = old_stdout
867
+ else:
868
+ for g_key, name, gate_fn in gates:
869
+ if gate_toggles.get(g_key) is False:
870
+ print(f"\n[{name.split(':')[0].upper()}] Disabled via synapse.config.json (SKIPPED)")
871
+ gate_results.append((name, True, "SKIPPED_BY_CONFIG"))
872
+ continue
873
+
874
+ success = gate_fn(repo_root, mode=effective_mode)
875
+ gate_results.append((name, success, "PASS" if success else "FAIL"))
876
+ if not success:
877
+ all_passed = False
878
+
879
+ if output_format == "json":
880
+ res_doc = {
881
+ "timestamp": datetime.now().isoformat(),
882
+ "target": repo_root,
883
+ "mode": effective_mode,
884
+ "all_passed": all_passed,
885
+ "gates": [{"name": g[0], "passed": g[1], "status": g[2]} for g in gate_results]
886
+ }
887
+ print(json.dumps(res_doc, indent=2))
888
+ return 0 if all_passed else 1
889
+
890
+ if output_format == "junit":
891
+ print(format_gates_junit(gate_results, repo_root))
892
+ return 0 if all_passed else 1
533
893
 
534
894
  print("\n" + "=" * 70)
535
895
  if all_passed:
@@ -544,5 +904,29 @@ def main():
544
904
  return 1
545
905
 
546
906
 
907
+ def main():
908
+ parser = argparse.ArgumentParser(description="Enterprise DoD Verification Sentinel")
909
+ parser.add_argument("target", nargs="?", default=None,
910
+ help="Target repository path to verify (defaults to auto-detection from CWD or Hub)")
911
+ parser.add_argument("--repo", "--path", dest="repo_path", default=None,
912
+ help="Target repository path to verify")
913
+ parser.add_argument("--mode", choices=["auto", "hub", "target"], default="auto",
914
+ help="Execution mode (auto-detect, hub, or target test project)")
915
+ parser.add_argument("--format", choices=["text", "json", "junit"], default="text",
916
+ help="Output report format (default: text)")
917
+ parser.add_argument("--config", help="Path to synapse.config.json")
918
+ parser.add_argument("--test-cmd", default="",
919
+ help="Optional test command for Gate 8 dynamic mutation testing (e.g. 'pytest' or 'npx playwright test')")
920
+ args = parser.parse_args()
921
+
922
+ try:
923
+ repo_root = resolve_target_repo(cli_target=args.target, cli_repo=args.repo_path)
924
+ except FileNotFoundError as e:
925
+ print(f"[ERROR] {e}")
926
+ return 1
927
+
928
+ return run_all_gates(repo_root, mode=args.mode, test_cmd=args.test_cmd, output_format=args.format, config_path=args.config)
929
+
930
+
547
931
  if __name__ == "__main__":
548
932
  sys.exit(main())