synapse-hub 2.5.0 → 2.7.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 (92) hide show
  1. package/.agents/agents/failure-analyst.md +17 -0
  2. package/.agents/agents/orchestrator.md +20 -2
  3. package/.agents/agents/pom-crafter.md +20 -0
  4. package/.agents/agents/quality-gatekeeper.md +20 -2
  5. package/.agents/agents/test-runner.md +16 -0
  6. package/.agents/rules/context-engineering.md +22 -102
  7. package/.agents/rules/git-push-protocol.md +1 -1
  8. package/.agents/rules/graphify.md +2 -0
  9. package/.agents/rules/mandatory-playwright-live.md +9 -9
  10. package/.agents/rules/mobile_appium_rules.md +27 -116
  11. package/.agents/rules/performance_testing_rules.md +18 -62
  12. package/.agents/rules/skill-precedence.md +22 -42
  13. package/.agents/rules/systematic-debugging.md +10 -29
  14. package/.agents/skills/actor-qa-swarm/SKILL.md +77 -0
  15. package/.agents/skills/actor-qa-swarm/examples/broker_sharding_sample.json +12 -0
  16. package/.agents/skills/agentless-qa-repair/SKILL.md +69 -0
  17. package/.agents/skills/agentless-qa-repair/examples/healing_report_sample.json +17 -0
  18. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +1 -1
  19. package/.agents/skills/clean-pom-crafter/SKILL.md +2 -2
  20. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +4 -4
  21. package/.agents/skills/event-stream-qa-orchestrator/SKILL.md +79 -0
  22. package/.agents/skills/event-stream-qa-orchestrator/examples/timeline_sample.jsonl +2 -0
  23. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +2 -2
  24. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +6 -21
  25. package/.agents/skills/katalon-cli-runner/SKILL.md +1 -1
  26. package/.agents/skills/mipro-prompt-compiler/SKILL.md +59 -0
  27. package/.agents/skills/mipro-prompt-compiler/examples/compiled_module_sample.json +22 -0
  28. package/.agents/skills/qa-aci-interface/SKILL.md +67 -0
  29. package/.agents/skills/qa-aci-interface/examples/surgical_edit_sample.json +12 -0
  30. package/.agents/skills/qa-memory-os/SKILL.md +80 -0
  31. package/.agents/skills/qa-memory-os/examples/archival_memory_sample.json +21 -0
  32. package/.agents/skills/review-spec/SKILL.md +60 -0
  33. package/.agents/skills/review-spec/examples/sample_spec_review.md +33 -0
  34. package/.agents/skills/synthetic-test-data-generator/SKILL.md +2 -2
  35. package/.agents/skills/token-and-context-optimizer/references/context_engineering_guide.md +49 -0
  36. package/.agents/skills/tot-exploratory-testing/SKILL.md +59 -0
  37. package/.agents/skills/tot-exploratory-testing/examples/tot_graph_sample.json +35 -0
  38. package/.agents/skills/wcag-audit-patterns/SKILL.md +2 -2
  39. package/.agents/skills/web-agent-gym-benchmarking/SKILL.md +62 -0
  40. package/.agents/skills/web-agent-gym-benchmarking/examples/benchmark_report_sample.json +22 -0
  41. package/AGENTS.md +6 -1
  42. package/CLAUDE.md +1 -1
  43. package/NOTICE +40 -4
  44. package/README.en.md +27 -11
  45. package/README.md +28 -12
  46. package/docs/ARCHITECTURE.md +39 -11
  47. package/docs/adopted-ecosystem-tools.md +9 -0
  48. package/docs/research/EXTERNAL_AGENT_SKILLS_SURVEY.md +1062 -0
  49. package/docs/research/agent_skill_training_analysis.md +1357 -0
  50. package/package.json +1 -1
  51. package/skills_manifest.json +88 -7
  52. package/synapse.config.json +3 -2
  53. package/templates/ai_bridges/CLAUDE.md +1 -1
  54. package/templates/welcome.html.tmpl +34 -25
  55. package/tools/TOOLS_RUNBOOK.md +130 -1
  56. package/tools/__init__.py +1 -1
  57. package/tools/_openapi_parser.py +158 -0
  58. package/tools/agentless_patcher.py +417 -0
  59. package/tools/analyze_project.py +9 -6
  60. package/tools/auto_heal.py +29 -0
  61. package/tools/auto_heal_reflexion.py +172 -0
  62. package/tools/autogen_qa_broker.py +318 -0
  63. package/tools/browser_helpers.js +57 -2
  64. package/tools/browsergym_eval.py +237 -0
  65. package/tools/ci_healing_hook.py +1 -1
  66. package/tools/cli.py +3 -3
  67. package/tools/dashboard_generator.py +29 -13
  68. package/tools/db_sandbox.py +4 -0
  69. package/tools/dspy_qa_signatures.py +357 -0
  70. package/tools/heal_pr_automator.py +25 -0
  71. package/tools/interview_story.py +117 -0
  72. package/tools/lint_ai_docs.py +1 -1
  73. package/tools/mcp_excel_server.py +1 -1
  74. package/tools/mcp_server.py +1 -1
  75. package/tools/mipro_assertion_tuner.py +207 -0
  76. package/tools/pinpoint_failure.py +12 -0
  77. package/tools/project_state.py +4 -0
  78. package/tools/qa_event_stream.py +336 -0
  79. package/tools/reversible_sandbox.py +161 -0
  80. package/tools/safety_firewall.py +28 -0
  81. package/tools/skill_collision_guard.py +231 -0
  82. package/tools/skill_vault.py +343 -0
  83. package/tools/swe_qa_aci.py +369 -0
  84. package/tools/synapse_qa_bench.py +190 -0
  85. package/tools/sync_agent_rules.py +1 -1
  86. package/tools/test_memory_os.py +409 -0
  87. package/tools/textgrad_mutation_optimizer.py +234 -0
  88. package/tools/tot_exploratory_tester.py +240 -0
  89. package/tools/update_sentinel.py +3 -3
  90. package/tools/verify_all.py +35 -4
  91. package/tools/watch_mode.py +1 -1
  92. package/tools/welcome_page.py +2 -2
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-FileCopyrightText: Copyright (c) 2026 Thế Vị (@TeeVee998). All rights reserved.
3
+ # SPDX-License-Identifier: MIT
4
+ # Project: Synapse-Hub (https://github.com/TeeVee998/Synapse-Hub)
5
+
6
+ """
7
+ Tree/Graph-of-Thoughts Exploratory Testing Engine (Princeton ToT & ETH GoT Pattern)
8
+ ----------------------------------------------------------------------------------
9
+ Expands linear user stories into deliberative non-linear exploration trees:
10
+ 1. Multi-Branch Deliberation: Generates Happy Path, Boundary/Race, and Negative/Chaos branches.
11
+ 2. Risk-Scored Beam Search: Evaluates thought states and prunes low-risk tautological paths.
12
+ 3. Clean POM Synthesis: Produces structured test scenarios ready for Clean POM Page Classes.
13
+ 4. Multi-Hypothesis RCA: Diagnoses test failures across competing failure hypotheses (DOM_TIMEOUT, ASYNC_RACE, DB_LOCK, BACKEND_500).
14
+
15
+ Usage:
16
+ python tools/tot_exploratory_tester.py expand-story --story-file docs/stories/US_PAYMENT.md --max-depth 3 --beam-width 3
17
+ python tools/tot_exploratory_tester.py rca-hypotheses --error-log reports/failed.log
18
+ """
19
+
20
+ import os
21
+ import sys
22
+ import re
23
+ import json
24
+ import time
25
+ import argparse
26
+ from typing import Dict, Any, List, Optional, Tuple
27
+
28
+ # Enforce UTF-8 stdout encoding
29
+ if hasattr(sys.stdout, 'reconfigure'):
30
+ try:
31
+ sys.stdout.reconfigure(encoding='utf-8')
32
+ sys.stderr.reconfigure(encoding='utf-8')
33
+ except Exception:
34
+ pass
35
+
36
+
37
+ class TotExploratoryTester:
38
+ """Tree-of-Thoughts Engine for Exploratory QA and Root Cause Analysis."""
39
+
40
+ def __init__(self, repo_root: Optional[str] = None):
41
+ self.repo_root = repo_root or os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
42
+
43
+ def expand_story(self, story_content: str, max_depth: int = 3, beam_width: int = 3) -> Dict[str, Any]:
44
+ """Decomposes story into a 3-branch Tree of Thoughts with risk scores."""
45
+ # Extract title and criteria
46
+ lines = [l.strip() for l in story_content.splitlines() if l.strip()]
47
+ title = lines[0] if lines else "Untitled User Story"
48
+ if title.startswith("#"):
49
+ title = title.lstrip("#").strip()
50
+
51
+ # Extract keywords
52
+ keywords = re.findall(r'\b[a-zA-Z]{4,}\b', story_content.lower())
53
+ is_checkout = any(k in keywords for k in ["cart", "checkout", "pay", "order", "price"])
54
+ is_auth = any(k in keywords for k in ["login", "auth", "token", "password", "sso"])
55
+
56
+ context_domain = "FINANCIAL_CHECKOUT" if is_checkout else ("AUTH_SESSION" if is_auth else "GENERAL_CRUD")
57
+
58
+ # 3 Branches:
59
+ # 1. Happy Path
60
+ # 2. Boundary / Session Interruption / Race Condition
61
+ # 3. Negative / Chaos / Backend Fault
62
+ branches = [
63
+ {
64
+ "branch_id": "BRANCH_HAPPY_PATH",
65
+ "name": "Standard Happy Business Path",
66
+ "risk_score": 0.25,
67
+ "thoughts": [
68
+ {"step": 1, "thought": "Navigate to initial entity view and ensure clean pre-conditions."},
69
+ {"step": 2, "thought": "Populate valid payload attributes and trigger submit action."},
70
+ {"step": 3, "thought": "Verify success confirmation modal and assert database record creation."}
71
+ ]
72
+ },
73
+ {
74
+ "branch_id": "BRANCH_BOUNDARY_RACE",
75
+ "name": "Boundary, Session Interruption & Concurrency Path",
76
+ "risk_score": 0.88,
77
+ "thoughts": [
78
+ {"step": 1, "thought": "Simulate session expiration or token invalidation mid-flow."},
79
+ {"step": 2, "thought": "Perform rapid double-click on submission action to probe race condition."},
80
+ {"step": 3, "thought": "Assert idempotency: strictly 1 transaction created, duplicate blocked gracefully."}
81
+ ]
82
+ },
83
+ {
84
+ "branch_id": "BRANCH_NEGATIVE_CHAOS",
85
+ "name": "Negative Error Handling & Chaos Fault Path",
86
+ "risk_score": 0.72,
87
+ "thoughts": [
88
+ {"step": 1, "thought": "Inject malformed/boundary input payload exceeding maximum character limits."},
89
+ {"step": 2, "thought": "Simulate backend 500 error or database connection timeout via route stub."},
90
+ {"step": 3, "thought": "Assert user-friendly error toast, zero data corruption, and retry availability."}
91
+ ]
92
+ }
93
+ ]
94
+
95
+ # Beam search selection: sort by risk score descending and take beam_width
96
+ sorted_branches = sorted(branches, key=lambda b: b["risk_score"], reverse=True)
97
+ selected_branches = sorted_branches[:beam_width]
98
+
99
+ return {
100
+ "status": "PASS",
101
+ "story_title": title,
102
+ "domain": context_domain,
103
+ "max_depth": max_depth,
104
+ "beam_width": beam_width,
105
+ "total_branches_explored": len(branches),
106
+ "highest_risk_branch": selected_branches[0]["branch_id"],
107
+ "branches": selected_branches,
108
+ "clean_pom_recommendations": [
109
+ f"Implement {context_domain}Page extending BasePage",
110
+ "Encapsulate double-click debounce in clickSubmitSafe()",
111
+ "Assert boundary toast in verifyErrorMessageDisplayed()"
112
+ ]
113
+ }
114
+
115
+ def rca_hypotheses(self, error_log: str, hypotheses: Optional[List[str]] = None) -> Dict[str, Any]:
116
+ """Performs multi-hypothesis Root Cause Analysis on test failures."""
117
+ if not hypotheses:
118
+ hypotheses = ["DOM_TIMEOUT", "ASYNC_RACE", "DB_LOCK", "BACKEND_500", "STALE_SELECTOR"]
119
+
120
+ log_lower = error_log.lower()
121
+ scored_hypotheses = []
122
+
123
+ for hyp in hypotheses:
124
+ score = 0.0
125
+ evidence = []
126
+
127
+ if hyp == "DOM_TIMEOUT":
128
+ if "timed out" in log_lower or "waitfortimeout" in log_lower or "5000ms" in log_lower:
129
+ score += 0.85
130
+ evidence.append("Found timeout or waiting duration signature in log")
131
+ if "element is not visible" in log_lower or "tobevisible" in log_lower:
132
+ score += 0.10
133
+ evidence.append("Element visibility expectation failed")
134
+
135
+ elif hyp == "ASYNC_RACE":
136
+ if "click intercepted" in log_lower or "animation" in log_lower or "detached" in log_lower:
137
+ score += 0.90
138
+ evidence.append("Overlay or detachment signature points to async race condition")
139
+ if "networkidle" in log_lower or "pending" in log_lower:
140
+ score += 0.15
141
+ evidence.append("Pending network requests active during action")
142
+
143
+ elif hyp == "DB_LOCK":
144
+ if "deadlock" in log_lower or "lock" in log_lower or "sql" in log_lower or "unique constraint" in log_lower:
145
+ score += 0.88
146
+ evidence.append("Database lock or constraint collision detected in trace")
147
+
148
+ elif hyp == "BACKEND_500":
149
+ if re.search(r'\b500\b', log_lower) and not re.search(r'\b500\d+ms\b', log_lower) and ("http" in log_lower or "status" in log_lower or "server error" in log_lower or "internal" in log_lower):
150
+ score += 0.95
151
+ evidence.append("HTTP 500 status code explicitly logged")
152
+ elif "internal server error" in log_lower:
153
+ score += 0.95
154
+ evidence.append("Internal Server Error explicitly logged")
155
+
156
+ elif hyp == "STALE_SELECTOR":
157
+ if "locator" in log_lower or "selector" in log_lower or "xpath" in log_lower:
158
+ score += 0.70
159
+ evidence.append("Locator or selector syntax mentioned in failure message")
160
+
161
+ # Baseline confidence
162
+ if not evidence:
163
+ score = 0.10
164
+ evidence.append("No direct signature found; baseline background likelihood")
165
+
166
+ scored_hypotheses.append({
167
+ "hypothesis": hyp,
168
+ "confidence_score": round(min(1.0, score), 3),
169
+ "evidence": evidence
170
+ })
171
+
172
+ scored_hypotheses.sort(key=lambda h: h["confidence_score"], reverse=True)
173
+ winner = scored_hypotheses[0]
174
+
175
+ recommendations = {
176
+ "DOM_TIMEOUT": "Replace hardcoded delay with page.waitForLoadState('networkidle') or element.waitFor({state: 'visible'})",
177
+ "ASYNC_RACE": "Use clickSafe() helper with overlay detachment waiting or dispatchEvent('click')",
178
+ "DB_LOCK": "Randomize entity code prefix via DataGenerator.uniqueCode('AUTO_') and enforce teardown cleanup",
179
+ "BACKEND_500": "Inspect backend server logs for uncaught 500 NullPointerException or DB connection pool exhaustion",
180
+ "STALE_SELECTOR": "Run tools/agentless_patcher.py heal-failure to update stale attribute to ARIA role"
181
+ }
182
+
183
+ return {
184
+ "status": "PASS",
185
+ "primary_root_cause": winner["hypothesis"],
186
+ "confidence": winner["confidence_score"],
187
+ "recommended_fix": recommendations.get(winner["hypothesis"], "Review stacktrace with team"),
188
+ "ranked_hypotheses": scored_hypotheses
189
+ }
190
+
191
+
192
+ def main():
193
+ parser = argparse.ArgumentParser(description="Tree-of-Thoughts Exploratory Testing Engine")
194
+ subparsers = parser.add_subparsers(dest="subcommand", help="Subcommand to execute")
195
+
196
+ # expand-story
197
+ exp_p = subparsers.add_parser("expand-story", help="Expand User Story into Tree of Thoughts")
198
+ exp_p.add_argument("--story-file", required=True, help="Path to story markdown file or text string")
199
+ exp_p.add_argument("--max-depth", type=int, default=3, help="Max thought tree depth")
200
+ exp_p.add_argument("--beam-width", type=int, default=3, help="Beam search width")
201
+ exp_p.add_argument("--out-graph", help="Optional path to output graph JSON")
202
+
203
+ # rca-hypotheses
204
+ rca_p = subparsers.add_parser("rca-hypotheses", help="Diagnose failure via multi-hypothesis RCA")
205
+ rca_p.add_argument("--error-log", required=True, help="Path to error log or log string")
206
+ rca_p.add_argument("--hypotheses", help="Comma-separated hypotheses list")
207
+
208
+ args = parser.parse_args()
209
+ tester = TotExploratoryTester()
210
+
211
+ if args.subcommand == "expand-story":
212
+ content = args.story_file
213
+ if os.path.exists(args.story_file):
214
+ with open(args.story_file, "r", encoding="utf-8", errors="replace") as f:
215
+ content = f.read()
216
+ res = tester.expand_story(content, max_depth=args.max_depth, beam_width=args.beam_width)
217
+ if args.out_graph:
218
+ os.makedirs(os.path.dirname(os.path.abspath(args.out_graph)), exist_ok=True)
219
+ with open(args.out_graph, "w", encoding="utf-8") as f:
220
+ json.dump(res, f, indent=2)
221
+ print(json.dumps(res, indent=2))
222
+ sys.exit(0)
223
+
224
+ elif args.subcommand == "rca-hypotheses":
225
+ log_content = args.error_log
226
+ if os.path.exists(args.error_log):
227
+ with open(args.error_log, "r", encoding="utf-8", errors="replace") as f:
228
+ log_content = f.read()
229
+ hyp_list = [h.strip() for h in args.hypotheses.split(",")] if args.hypotheses else None
230
+ res = tester.rca_hypotheses(error_log=log_content, hypotheses=hyp_list)
231
+ print(json.dumps(res, indent=2))
232
+ sys.exit(0)
233
+
234
+ else:
235
+ parser.print_help()
236
+ sys.exit(1)
237
+
238
+
239
+ if __name__ == "__main__":
240
+ main()
@@ -65,7 +65,7 @@ def get_local_version():
65
65
  try:
66
66
  with open(pkg_file, "r", encoding="utf-8") as f:
67
67
  data = json.load(f)
68
- return data.get("version", "2.5.0")
68
+ return data.get("version", "2.7.0")
69
69
  except (OSError, json.JSONDecodeError):
70
70
  data = {}
71
71
 
@@ -74,11 +74,11 @@ def get_local_version():
74
74
  try:
75
75
  with open(manifest_file, "r", encoding="utf-8") as f:
76
76
  data = json.load(f)
77
- return data.get("version", "2.5.0")
77
+ return data.get("version", "2.7.0")
78
78
  except (OSError, json.JSONDecodeError):
79
79
  data = {}
80
80
 
81
- return "2.5.0"
81
+ return "2.7.0"
82
82
 
83
83
 
84
84
  def fetch_remote_version():
@@ -18,6 +18,7 @@ Verifies:
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
20
  Gate 8: Syntactic Micro-Mutation Sentinel: Injects syntactic faults to eliminate tautological tests (Mutation Score >= 80%).
21
+ Gate 9: Tier-2 Skill Collision Guard: Addy Osmani offline TF-IDF cosine similarity check (similarity < 0.75).
21
22
 
22
23
  Supports:
23
24
  - Hub Mode: Verifies the centralized QA Hub / skills repository integrity.
@@ -194,7 +195,7 @@ MOJIBAKE_INDICATORS = (
194
195
 
195
196
  def gate_1_bom_check(repo_root, mode="AUTO"):
196
197
  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_dirs = {".git", ".gradle", "node_modules", "allure-results", "graphify-out", "__pycache__", "build", "dist", "claude-ecosystem-repos", "external_repos", "teamwork"}
198
199
  ignored_files = {"verify_all.py", "test_tools.py"}
199
200
  bom_violations = []
200
201
  mojibake_violations = []
@@ -244,7 +245,7 @@ def gate_2_path_portability(repo_root, mode="AUTO"):
244
245
  ignored_dirs = {
245
246
  ".git", ".gradle", "node_modules", "allure-results", "Screenshots",
246
247
  "Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings", "graphify-out",
247
- "__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos"
248
+ "__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos", "external_repos", "teamwork"
248
249
  }
249
250
  ignored_files = {
250
251
  ".project", ".classpath", ".DS_Store", "memory_cache.db",
@@ -441,7 +442,7 @@ def gate_6_credential_security(repo_root, mode="AUTO"):
441
442
  ignored_dirs = {
442
443
  ".git", ".gradle", "node_modules", "allure-results", "Screenshots",
443
444
  "Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings", "graphify-out",
444
- "__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos"
445
+ "__pycache__", "agent_qa.egg-info", "dist", "build", "claude-ecosystem-repos", "external_repos", "teamwork"
445
446
  }
446
447
 
447
448
  for root, dirs, files in os.walk(repo_root):
@@ -480,7 +481,7 @@ def check_python_ast_integrity(repo_root):
480
481
  """
481
482
  builtin_names = set(dir(builtins)) | {'__file__', '__name__', '__doc__', '__package__'}
482
483
  py_files = []
483
- ignored = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'claude-ecosystem-repos', 'build', 'dist', '.gradle'}
484
+ ignored = {'.git', 'node_modules', '__pycache__', '.venv', 'venv', 'claude-ecosystem-repos', 'build', 'dist', '.gradle', 'external_repos', 'teamwork'}
484
485
 
485
486
  for root, dirs, files in os.walk(repo_root):
486
487
  dirs[:] = [d for d in dirs if d not in ignored]
@@ -795,6 +796,35 @@ def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
795
796
  return True
796
797
 
797
798
 
799
+ def gate_9_skill_collision(repo_root, mode="AUTO"):
800
+ print("\n[GATE 9] Running Tier-2 Skill Routing & Collision Guard (Addy Osmani Pattern)...")
801
+ guard_script = os.path.join(SCRIPT_DIR, "skill_collision_guard.py")
802
+ if not os.path.exists(guard_script):
803
+ guard_script = os.path.join(repo_root, "tools", "skill_collision_guard.py")
804
+ if not os.path.exists(guard_script):
805
+ print("[FAIL] skill_collision_guard.py not found in tools/!")
806
+ return False
807
+
808
+ skills_dir = os.path.join(repo_root, ".agents", "skills")
809
+ if not os.path.exists(skills_dir):
810
+ print("[PASS] Target project mode: .agents/skills/ not present (SKIPPED).")
811
+ return True
812
+
813
+ try:
814
+ res = subprocess.run([sys.executable, guard_script, "--dir", skills_dir, "--threshold", "0.75"],
815
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=60.0)
816
+ except subprocess.TimeoutExpired:
817
+ print("[FAIL] skill_collision_guard.py timed out after 60s!")
818
+ return False
819
+
820
+ if res.returncode != 0:
821
+ print(res.stdout)
822
+ print(res.stderr)
823
+ return False
824
+ print("[PASS] 100% Verified: 0 skill description collisions detected (similarity < 0.75).")
825
+ return True
826
+
827
+
798
828
  def load_synapse_config(repo_root=None, config_path=None):
799
829
  """Loads configuration from synapse.config.json if present."""
800
830
  if config_path and os.path.exists(config_path):
@@ -855,6 +885,7 @@ def run_all_gates(repo_root, mode="AUTO", test_cmd="", output_format="text", con
855
885
  ("gate_6_credential_security", "Gate 6: Credential Security", gate_6_credential_security),
856
886
  ("gate_7_infer_linter", "Gate 7: Static Defect Sentinel", gate_7_infer_linter),
857
887
  ("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)),
888
+ ("gate_9_skill_collision", "Gate 9: Tier-2 Skill Routing & Collision Guard", gate_9_skill_collision),
858
889
  ]
859
890
 
860
891
  all_passed = True
@@ -46,7 +46,7 @@ IGNORED_DIRS = {
46
46
  ".git", ".gradle", "node_modules", "allure-results", "Screenshots",
47
47
  "Reports", "reports", "TestReports", "testreports", "bin", "Libs", ".settings",
48
48
  "graphify-out", "__pycache__", "agent_qa.egg-info", "dist", "build", ".pytest_cache",
49
- "claude-ecosystem-repos"
49
+ "claude-ecosystem-repos", "external_repos", "teamwork"
50
50
  }
51
51
 
52
52
  IGNORED_FILES = {
@@ -17,7 +17,7 @@ Features:
17
17
  - Interactive tabs covering the 4 Core Pillars of QA Intelligence.
18
18
  - Interactive CLI Cheatsheet with 1-click clipboard copy.
19
19
  - Comparison matrix vs existing industry tools (ESLint, Stryker, Allure, Healenium).
20
- - 6 Subagent Swarm Profiles & 9 Quality Gates DoD showcase.
20
+ - 6 Subagent Swarm Profiles & 10 Quality Gates DoD showcase.
21
21
  - Automatic browser launching on setup, pull, or CLI dispatch.
22
22
  """
23
23
 
@@ -48,7 +48,7 @@ def get_hub_metadata() -> Dict[str, Any]:
48
48
  """Retrieves version and catalog count from skills_manifest.json."""
49
49
  manifest_path = os.path.join(HUB_ROOT, "skills_manifest.json")
50
50
  meta = {
51
- "version": "2.5.0",
51
+ "version": "2.7.0",
52
52
  "totalSkills": 42,
53
53
  "author": "Thế Vị (@TeeVee998)",
54
54
  "repo": "https://github.com/TeeVee998/Synapse-Hub"