synapse-hub 2.6.0 → 3.0.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 (143) hide show
  1. package/.agents/agents/failure-analyst.md +4 -0
  2. package/.agents/agents/live-explorer.md +2 -0
  3. package/.agents/agents/orchestrator.md +16 -0
  4. package/.agents/agents/pom-crafter.md +10 -0
  5. package/.agents/agents/quality-gatekeeper.md +5 -0
  6. package/.agents/agents/test-runner.md +8 -0
  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 +8 -1
  11. package/.agents/skills/actor-qa-swarm/SKILL.md +77 -0
  12. package/.agents/skills/actor-qa-swarm/examples/broker_sharding_sample.json +12 -0
  13. package/.agents/skills/agentless-qa-repair/SKILL.md +69 -0
  14. package/.agents/skills/agentless-qa-repair/examples/healing_report_sample.json +17 -0
  15. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +1 -1
  16. package/.agents/skills/clean-pom-crafter/SKILL.md +2 -2
  17. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +4 -4
  18. package/.agents/skills/event-stream-qa-orchestrator/SKILL.md +79 -0
  19. package/.agents/skills/event-stream-qa-orchestrator/examples/timeline_sample.jsonl +2 -0
  20. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +2 -2
  21. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +6 -21
  22. package/.agents/skills/katalon-cli-runner/SKILL.md +1 -1
  23. package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
  24. package/.agents/skills/mipro-prompt-compiler/SKILL.md +59 -0
  25. package/.agents/skills/mipro-prompt-compiler/examples/compiled_module_sample.json +22 -0
  26. package/.agents/skills/qa-aci-interface/SKILL.md +67 -0
  27. package/.agents/skills/qa-aci-interface/examples/surgical_edit_sample.json +12 -0
  28. package/.agents/skills/qa-memory-os/SKILL.md +80 -0
  29. package/.agents/skills/qa-memory-os/examples/archival_memory_sample.json +21 -0
  30. package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
  31. package/.agents/skills/review-spec/SKILL.md +60 -0
  32. package/.agents/skills/review-spec/examples/sample_spec_review.md +33 -0
  33. package/.agents/skills/senior-code-review/SKILL.md +1 -1
  34. package/.agents/skills/synthetic-test-data-generator/SKILL.md +2 -2
  35. package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
  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/CLAUDE.md +2 -2
  42. package/README.en.md +70 -291
  43. package/README.md +68 -303
  44. package/docs/ARCHITECTURE.md +54 -8
  45. package/docs/CONTRIBUTING.md +1 -1
  46. package/docs/QUALITY_GATES.md +113 -0
  47. package/docs/research/EXTERNAL_AGENT_SKILLS_SURVEY.md +1062 -0
  48. package/package.json +1 -1
  49. package/scripts/package_dist.py +2 -1
  50. package/skills_manifest.json +92 -11
  51. package/synapse.config.json +1 -1
  52. package/templates/ai_bridges/CLAUDE.md +2 -2
  53. package/templates/ai_bridges/copilot-instructions.md +2 -2
  54. package/tools/TOOLS_RUNBOOK.md +1 -1
  55. package/tools/__init__.py +18 -1
  56. package/tools/_openapi_parser.py +158 -0
  57. package/tools/agent_canary.py +42 -30
  58. package/tools/agentless_patcher.py +417 -0
  59. package/tools/analyze_project.py +12 -11
  60. package/tools/api_drift_sentinel.py +4 -4
  61. package/tools/api_fuzzer.py +8 -8
  62. package/tools/auto_heal.py +23 -5
  63. package/tools/auto_heal_reflexion.py +2 -2
  64. package/tools/autogen_qa_broker.py +318 -0
  65. package/tools/browser_helpers.js +2 -2
  66. package/tools/browsergym_eval.py +236 -0
  67. package/tools/bug_reporter.py +11 -13
  68. package/tools/bump_version.py +75 -5
  69. package/tools/cache_resolver.py +78 -83
  70. package/tools/check_env.py +9 -1
  71. package/tools/check_version_sync.py +40 -3
  72. package/tools/ci_healing_hook.py +5 -7
  73. package/tools/cli.py +5 -7
  74. package/tools/config_decoupler.py +2 -3
  75. package/tools/core/__init__.py +52 -0
  76. package/tools/count_tokens.py +22 -20
  77. package/tools/dashboard_generator.py +628 -999
  78. package/tools/db_sandbox.py +69 -53
  79. package/tools/debt_harvester.py +3 -3
  80. package/tools/distill_log.py +3 -3
  81. package/tools/dspy_qa_signatures.py +3 -4
  82. package/tools/export_postman_collection.py +8 -8
  83. package/tools/flaky_detector.py +2 -2
  84. package/tools/fuzz_payloads.py +5 -5
  85. package/tools/generate_allure_report.py +1 -1
  86. package/tools/generate_jmx_plan.py +4 -4
  87. package/tools/gherkin_suite.py +1 -1
  88. package/tools/git_pre_commit_guard.py +2 -2
  89. package/tools/goldfish_review.py +2 -2
  90. package/tools/heal/__init__.py +28 -0
  91. package/tools/heal_pr_automator.py +29 -4
  92. package/tools/heuristic_locator_matcher.py +2 -2
  93. package/tools/infer_linter.py +8 -8
  94. package/tools/interview_story.py +0 -1
  95. package/tools/jtl_analyzer.py +11 -11
  96. package/tools/lint/__init__.py +30 -0
  97. package/tools/lint_ai_docs.py +42 -0
  98. package/tools/lookup_tc.py +1 -1
  99. package/tools/matrix_sharder.py +15 -13
  100. package/tools/mcp/__init__.py +16 -0
  101. package/tools/mcp_excel_server.py +2 -2
  102. package/tools/mcp_server.py +3 -4
  103. package/tools/md_to_xlsx.py +26 -26
  104. package/tools/micro_mutator.py +11 -12
  105. package/tools/mipro_assertion_tuner.py +207 -0
  106. package/tools/network_mock_shield.py +27 -27
  107. package/tools/newman_analyzer.py +2 -3
  108. package/tools/omni_emitter.py +5 -8
  109. package/tools/omni_parser.py +5 -5
  110. package/tools/omni_transpiler.py +5 -5
  111. package/tools/output_formatter.py +19 -0
  112. package/tools/perf/__init__.py +24 -0
  113. package/tools/pinpoint_failure.py +13 -1
  114. package/tools/project_state.py +4 -2
  115. package/tools/qa_event_stream.py +336 -0
  116. package/tools/repo_map.py +8 -8
  117. package/tools/report/__init__.py +32 -0
  118. package/tools/reversible_sandbox.py +5 -2
  119. package/tools/safety_firewall.py +2 -2
  120. package/tools/senior_code_review.py +2 -2
  121. package/tools/setup_project.py +10 -12
  122. package/tools/skill_collision_guard.py +3 -3
  123. package/tools/skill_vault.py +12 -3
  124. package/tools/skillspector.py +5 -7
  125. package/tools/smart_visual_masker.py +4 -6
  126. package/tools/spec_drift_sentinel.py +1 -2
  127. package/tools/swe_qa_aci.py +369 -0
  128. package/tools/synapse_qa_bench.py +3 -3
  129. package/tools/sync_agent_rules.py +18 -4
  130. package/tools/telemetry_correlator.py +3 -2
  131. package/tools/test_memory_os.py +419 -0
  132. package/tools/textgrad_mutation_optimizer.py +3 -3
  133. package/tools/tms_sync_bridge.py +5 -5
  134. package/tools/tot_exploratory_tester.py +239 -0
  135. package/tools/traffic_session_converter.py +9 -9
  136. package/tools/universal_ir.py +2 -3
  137. package/tools/update_sentinel.py +6 -9
  138. package/tools/verify_all.py +10 -0
  139. package/tools/visual_diff_canvas.py +1 -2
  140. package/tools/visual_som_helper.py +15 -13
  141. package/tools/watch_mode.py +1 -1
  142. package/tools/wcag_audit.py +22 -1
  143. package/tools/welcome_page.py +5 -6
@@ -42,12 +42,32 @@ if hasattr(sys.stdout, 'reconfigure'):
42
42
  REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
43
43
 
44
44
  def parse_semver(v_str: str):
45
+ """Parses a semantic version string into major, minor, patch integers and optional suffix.
46
+
47
+ Args:
48
+ v_str: The version string to parse (e.g. '2.9.0' or '2.9.0-rc1').
49
+
50
+ Returns:
51
+ A tuple of (major, minor, patch, suffix).
52
+
53
+ Raises:
54
+ ValueError: If the string does not follow semantic versioning.
55
+ """
45
56
  m = re.match(r"^(\d+)\.(\d+)\.(\d+)(.*)$", v_str.strip())
46
57
  if not m:
47
58
  raise ValueError(f"Invalid semver: '{v_str}'. Must follow major.minor.patch[-suffix]")
48
59
  return int(m.group(1)), int(m.group(2)), int(m.group(3)), m.group(4)
49
60
 
50
61
  def calculate_next_version(current: str, action: str) -> str:
62
+ """Computes the target version based on an action or explicit semver string.
63
+
64
+ Args:
65
+ current: Current semver version string.
66
+ action: 'patch', 'minor', 'major', or an explicit target version string.
67
+
68
+ Returns:
69
+ The computed next semver version string.
70
+ """
51
71
  major, minor, patch, suffix = parse_semver(current)
52
72
  action = action.lower()
53
73
  if action == "patch":
@@ -62,6 +82,14 @@ def calculate_next_version(current: str, action: str) -> str:
62
82
  return action
63
83
 
64
84
  def get_current_version() -> str:
85
+ """Reads the current canonical version from package.json in the repository root.
86
+
87
+ Returns:
88
+ The version string.
89
+
90
+ Raises:
91
+ ValueError: If package.json is missing or lacks a valid version field.
92
+ """
65
93
  pkg_file = os.path.join(REPO_ROOT, "package.json")
66
94
  with open(pkg_file, "r", encoding="utf-8") as f:
67
95
  data = json.load(f)
@@ -71,6 +99,16 @@ def get_current_version() -> str:
71
99
  return version
72
100
 
73
101
  def update_json_root_version(rel_path: str, new_ver: str, dry_run: bool = False) -> bool:
102
+ """Updates the top-level 'version' property in a JSON manifest file.
103
+
104
+ Args:
105
+ rel_path: Relative path to the target JSON file from repo root.
106
+ new_ver: Target version string.
107
+ dry_run: If True, previews changes without writing to disk.
108
+
109
+ Returns:
110
+ True if the file was modified or would be updated.
111
+ """
74
112
  full_path = os.path.join(REPO_ROOT, rel_path)
75
113
  if not os.path.exists(full_path):
76
114
  print(f" [WARN] File not found: {rel_path}")
@@ -95,6 +133,17 @@ def update_json_root_version(rel_path: str, new_ver: str, dry_run: bool = False)
95
133
  return True
96
134
 
97
135
  def update_file(rel_path: str, pattern: str, replacement: str, dry_run: bool = False) -> bool:
136
+ """Performs regex replacement within a target file to update version strings.
137
+
138
+ Args:
139
+ rel_path: Relative path to the target file from repo root.
140
+ pattern: Regular expression pattern matching existing version occurrences.
141
+ replacement: String replacement containing the new version.
142
+ dry_run: If True, previews changes without writing to disk.
143
+
144
+ Returns:
145
+ True if matches were found and updated.
146
+ """
98
147
  full_path = os.path.join(REPO_ROOT, rel_path)
99
148
  if not os.path.exists(full_path):
100
149
  print(f" [WARN] File not found: {rel_path}")
@@ -117,6 +166,11 @@ def update_file(rel_path: str, pattern: str, replacement: str, dry_run: bool = F
117
166
  return True
118
167
 
119
168
  def run_tests() -> bool:
169
+ """Executes the test suite and 10 DoD Quality Gates before release.
170
+
171
+ Returns:
172
+ True if all tests and gates pass.
173
+ """
120
174
  print("\n🔍 Running verification suite...")
121
175
  cmd = [sys.executable, "-m", "unittest", "discover", "tests", "-p", "test_*.py"]
122
176
  res = subprocess.run(cmd, cwd=REPO_ROOT)
@@ -130,10 +184,16 @@ def run_tests() -> bool:
130
184
  if res_verify.returncode != 0:
131
185
  print("❌ Quality Gates failed! Aborting release.")
132
186
  return False
133
- print("✅ 9 Quality Gates DoD PASSED.")
187
+ print("✅ 10 Quality Gates DoD PASSED.")
134
188
  return True
135
189
 
136
190
  def git_commit_and_tag(version: str, push: bool = False):
191
+ """Creates a conventional Git release commit and tags the repository.
192
+
193
+ Args:
194
+ version: Target version string.
195
+ push: If True, pushes the commit and tag to git remote origin.
196
+ """
137
197
  print(f"\n📦 Creating Git commit and tag v{version}...")
138
198
  subprocess.run(["git", "add", "-A"], cwd=REPO_ROOT, check=True)
139
199
  subprocess.run(["git", "commit", "-m", f"chore(release): bump version to v{version}"], cwd=REPO_ROOT, check=True)
@@ -141,7 +201,7 @@ def git_commit_and_tag(version: str, push: bool = False):
141
201
  print(f"✅ Git commit and tag v{version} created.")
142
202
 
143
203
  if push:
144
- print(f"🚀 Pushing commit and tags to remote origin...")
204
+ print("🚀 Pushing commit and tags to remote origin...")
145
205
  res = subprocess.run(["git", "branch", "--show-current"], cwd=REPO_ROOT, stdout=subprocess.PIPE, text=True)
146
206
  cur_branch = res.stdout.strip()
147
207
  if cur_branch == "develop":
@@ -154,9 +214,10 @@ def git_commit_and_tag(version: str, push: bool = False):
154
214
  subprocess.run(["git", "push", "origin", "HEAD", "--tags"], cwd=REPO_ROOT, check=True)
155
215
  else:
156
216
  subprocess.run(["git", "push", "origin", "HEAD", "--tags"], cwd=REPO_ROOT, check=True)
157
- print(f"✅ Remote origin synchronized.")
217
+ print("✅ Remote origin synchronized.")
158
218
 
159
219
  def main():
220
+ """CLI entrypoint for bumping Synapse Hub version synchronously across manifests."""
160
221
  parser = argparse.ArgumentParser(description="Atomic Universal Version Bumper for Synapse Hub")
161
222
  parser.add_argument("action", nargs="?", default="patch", help="patch | minor | major | <explicit_version> (default: patch)")
162
223
  parser.add_argument("--dry-run", action="store_true", help="Preview version changes without writing files")
@@ -217,8 +278,17 @@ def main():
217
278
  update_file("README.md", r'(Phiên bản Hub:\*\*\s*`v)[^`]+(`)', rf"\g<1>{new_ver}\g<2>", args.dry_run)
218
279
  update_file("README.md", r'(Hub Version:\*\*\s*`v)[^`]+(`)', rf"\g<1>{new_ver}\g<2>", args.dry_run)
219
280
  update_file("README.md", r'(Intelligence Core v)[^")\]]+', rf"\g<1>{new_ver}", args.dry_run)
281
+ update_file("README.md", r'(rev:\s*v)[0-9]+\.[0-9]+\.[0-9]+', rf"\g<1>{new_ver}", args.dry_run)
282
+ update_file("README.md", r'(Synapse Hub \(Core v)[0-9]+\.[0-9]+\.[0-9]+(\))', rf"\g<1>{new_ver}\g<2>", args.dry_run)
220
283
  update_file("README.en.md", r'(Hub Version:\*\*\s*`v)[^`]+(`)', rf"\g<1>{new_ver}\g<2>", args.dry_run)
221
284
  update_file("README.en.md", r'(Intelligence Core v)[^")\]]+', rf"\g<1>{new_ver}", args.dry_run)
285
+ update_file("README.en.md", r'(rev:\s*v)[0-9]+\.[0-9]+\.[0-9]+', rf"\g<1>{new_ver}", args.dry_run)
286
+ update_file("README.en.md", r'(Synapse Hub \(Core v)[0-9]+\.[0-9]+\.[0-9]+(\))', rf"\g<1>{new_ver}\g<2>", args.dry_run)
287
+
288
+ # 9b. SECURITY.md
289
+ major, minor, _, _ = parse_semver(new_ver)
290
+ current_branch = f"{major}.{minor}.x"
291
+ update_file("SECURITY.md", r'(\|\s*`)[0-9]+\.[0-9]+\.x(`\s*\|\s*✅)', rf"\g<1>{current_branch}\g<2>", args.dry_run)
222
292
 
223
293
  # 10. tools/__init__.py
224
294
  update_file("tools/__init__.py", r'(__version__\s*=\s*")[^"]+(")', rf"\g<1>{new_ver}\g<2>", args.dry_run)
@@ -267,8 +337,8 @@ def main():
267
337
  print(f"🎉 VERSION BUMP COMPLETE: v{current_ver} ➔ v{new_ver}")
268
338
  print("======================================================================")
269
339
  print("Next steps:")
270
- print(f" • Publish to NPM : npm publish")
271
- print(f" • Or Git Push : git push origin HEAD --tags")
340
+ print(" • Publish to NPM : npm publish")
341
+ print(" • Or Git Push : git push origin HEAD --tags")
272
342
  print("======================================================================")
273
343
 
274
344
  if __name__ == "__main__":
@@ -313,7 +313,7 @@ def init_db(seed=True, target_db=None):
313
313
  try:
314
314
  cursor.execute("ALTER TABLE healing_cache ADD COLUMN code_patch TEXT DEFAULT ''")
315
315
  except sqlite3.OperationalError:
316
- column_exists = True
316
+ _ = True
317
317
 
318
318
  # 2. Negative Memory Table (Blacklist of Failed Fix Attempts)
319
319
  cursor.execute("""
@@ -362,30 +362,29 @@ def init_db(seed=True, target_db=None):
362
362
  # Migrate existing solutions from local DB if initializing global DB for the first time
363
363
  if current_db == GLOBAL_DB and os.path.exists(LOCAL_DB):
364
364
  try:
365
- local_conn = get_db(LOCAL_DB)
366
- rows = local_conn.execute("SELECT * FROM healing_cache").fetchall()
367
- for r in rows:
368
- cursor.execute("""
369
- INSERT OR IGNORE INTO healing_cache
370
- (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
371
- VALUES (?, ?, ?, ?, ?, ?)
372
- """, (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
373
- conn.commit()
374
- local_conn.close()
365
+ with get_db(LOCAL_DB) as local_conn:
366
+ rows = local_conn.execute("SELECT * FROM healing_cache").fetchall()
367
+ for r in rows:
368
+ cursor.execute("""
369
+ INSERT OR IGNORE INTO healing_cache
370
+ (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
371
+ VALUES (?, ?, ?, ?, ?, ?)
372
+ """, (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
373
+ conn.commit()
375
374
  except (sqlite3.Error, OSError) as e:
376
375
  sys.stderr.write(f"Notice: Local cache migration skipped: {e}\n")
377
376
 
378
377
  if seed:
379
378
  for s in SEED_SOLUTIONS:
380
379
  cursor.execute("""
381
- INSERT OR IGNORE INTO healing_cache
380
+ INSERT OR IGNORE INTO healing_cache
382
381
  (error_signature, module_name, symptom, root_cause, groovy_patch)
383
382
  VALUES (?, ?, ?, ?, ?)
384
383
  """, (s["error_signature"], s["module_name"], s["symptom"], s["root_cause"], s["groovy_patch"]))
385
384
 
386
385
  for f in SEED_FAILED_FIXES:
387
386
  cursor.execute("""
388
- SELECT COUNT(*) as cnt FROM failed_fixes
387
+ SELECT COUNT(*) as cnt FROM failed_fixes
389
388
  WHERE error_signature = ? AND attempted_patch = ?
390
389
  """, (f["error_signature"], f["attempted_patch"]))
391
390
  if cursor.fetchone()["cnt"] == 0:
@@ -394,17 +393,17 @@ def init_db(seed=True, target_db=None):
394
393
  VALUES (?, ?, ?, ?)
395
394
  """, (f["error_signature"], f["attempted_patch"], f["failure_reason"], f["agent_role"]))
396
395
 
397
- for l in SEED_LESSONS_LEARNED:
396
+ for les in SEED_LESSONS_LEARNED:
398
397
  cursor.execute("""
399
- SELECT COUNT(*) as cnt FROM lessons_learned
398
+ SELECT COUNT(*) as cnt FROM lessons_learned
400
399
  WHERE error_signature = ? AND page_context = ?
401
- """, (l["error_signature"], l["page_context"]))
400
+ """, (les["error_signature"], les["page_context"]))
402
401
  if cursor.fetchone()["cnt"] == 0:
403
402
  cursor.execute("""
404
- INSERT INTO lessons_learned
403
+ INSERT INTO lessons_learned
405
404
  (page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary)
406
405
  VALUES (?, ?, ?, ?, ?, ?)
407
- """, (l["page_context"], l["error_signature"], l["root_cause"], l["failed_attempt"], l["successful_fix"], l["lesson_summary"]))
406
+ """, (les["page_context"], les["error_signature"], les["root_cause"], les["failed_attempt"], les["successful_fix"], les["lesson_summary"]))
408
407
  conn.commit()
409
408
 
410
409
 
@@ -552,7 +551,7 @@ def get_failed_fixes(error_signature):
552
551
  with get_db() as conn:
553
552
  cursor = conn.cursor()
554
553
  cursor.execute("""
555
- SELECT * FROM failed_fixes
554
+ SELECT * FROM failed_fixes
556
555
  WHERE error_signature = ? OR error_signature LIKE ?
557
556
  ORDER BY created_at DESC
558
557
  """, (error_signature, f"%{error_signature}%"))
@@ -587,7 +586,7 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
587
586
  cursor = conn.cursor()
588
587
  # 1. Exact match with module
589
588
  cursor.execute("""
590
- SELECT * FROM healing_cache
589
+ SELECT * FROM healing_cache
591
590
  WHERE error_signature = ? AND module_name = ?
592
591
  """, (error_signature, module_name))
593
592
  row = cursor.fetchone()
@@ -596,7 +595,7 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
596
595
  # 2. Fallback to GLOBAL match
597
596
  if not row:
598
597
  cursor.execute("""
599
- SELECT * FROM healing_cache
598
+ SELECT * FROM healing_cache
600
599
  WHERE error_signature = ? AND module_name = 'GLOBAL'
601
600
  """, (error_signature,))
602
601
  row = cursor.fetchone()
@@ -605,7 +604,7 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
605
604
  # 3. Fuzzy search on symptom/error
606
605
  if not row:
607
606
  cursor.execute("""
608
- SELECT * FROM healing_cache
607
+ SELECT * FROM healing_cache
609
608
  WHERE error_signature LIKE ? OR symptom LIKE ?
610
609
  """, (f"%{error_signature}%", f"%{error_signature}%"))
611
610
  row = cursor.fetchone()
@@ -639,8 +638,8 @@ def query_solution(error_signature, module_name="GLOBAL", threshold=0.35):
639
638
 
640
639
  if row:
641
640
  cursor.execute("""
642
- UPDATE healing_cache
643
- SET hit_count = hit_count + 1
641
+ UPDATE healing_cache
642
+ SET hit_count = hit_count + 1
644
643
  WHERE id = ?
645
644
  """, (row["id"],))
646
645
  conn.commit()
@@ -700,57 +699,53 @@ def sync_db():
700
699
  init_db(seed=True, target_db=GLOBAL_DB)
701
700
  DB_FILE = resolve_db_path(use_local=True)
702
701
  init_db(seed=False, target_db=LOCAL_DB)
703
-
704
- g_conn = get_db(GLOBAL_DB)
705
- l_conn = get_db(LOCAL_DB)
706
-
707
- # 1. Sync positive cache
708
- g_rows = g_conn.execute("SELECT * FROM healing_cache").fetchall()
709
- for r in g_rows:
710
- l_conn.execute("""
711
- INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
712
- VALUES (?, ?, ?, ?, ?, ?)
713
- ON CONFLICT(error_signature, module_name) DO UPDATE SET
714
- symptom = excluded.symptom,
715
- root_cause = excluded.root_cause,
716
- groovy_patch = excluded.groovy_patch
717
- """, (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
718
- l_conn.commit()
719
-
720
- l_rows = l_conn.execute("SELECT * FROM healing_cache").fetchall()
721
- for r in l_rows:
722
- g_conn.execute("""
723
- INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
724
- VALUES (?, ?, ?, ?, ?, ?)
725
- ON CONFLICT(error_signature, module_name) DO UPDATE SET
726
- symptom = excluded.symptom,
727
- root_cause = excluded.root_cause,
728
- groovy_patch = excluded.groovy_patch
729
- """, (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
730
- g_conn.commit()
731
-
732
- # 2. Sync negative failed fixes
733
- g_failed = g_conn.execute("SELECT * FROM failed_fixes").fetchall()
734
- for f in g_failed:
735
- l_conn.execute("""
736
- INSERT OR IGNORE INTO failed_fixes (error_signature, attempted_patch, failure_reason, agent_role)
737
- VALUES (?, ?, ?, ?)
738
- """, (f["error_signature"], f["attempted_patch"], f["failure_reason"], f["agent_role"]))
739
- l_conn.commit()
740
-
741
- # 3. Sync lessons learned
742
- g_lessons = g_conn.execute("SELECT * FROM lessons_learned").fetchall()
743
- for r in g_lessons:
744
- l_conn.execute("""
745
- INSERT OR IGNORE INTO lessons_learned
746
- (page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary, hit_count)
747
- VALUES (?, ?, ?, ?, ?, ?, ?)
748
- """, (r["page_context"], r["error_signature"], r["root_cause"], r["failed_attempt"], r["successful_fix"], r["lesson_summary"], r["hit_count"]))
749
- l_conn.commit()
750
702
 
751
- l_conn.close()
752
- g_conn.close()
753
- return len(g_rows)
703
+ with get_db(GLOBAL_DB) as g_conn, get_db(LOCAL_DB) as l_conn:
704
+ # 1. Sync positive cache
705
+ g_rows = g_conn.execute("SELECT * FROM healing_cache").fetchall()
706
+ for r in g_rows:
707
+ l_conn.execute("""
708
+ INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
709
+ VALUES (?, ?, ?, ?, ?, ?)
710
+ ON CONFLICT(error_signature, module_name) DO UPDATE SET
711
+ symptom = excluded.symptom,
712
+ root_cause = excluded.root_cause,
713
+ groovy_patch = excluded.groovy_patch
714
+ """, (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
715
+ l_conn.commit()
716
+
717
+ l_rows = l_conn.execute("SELECT * FROM healing_cache").fetchall()
718
+ for r in l_rows:
719
+ g_conn.execute("""
720
+ INSERT INTO healing_cache (error_signature, module_name, symptom, root_cause, groovy_patch, hit_count)
721
+ VALUES (?, ?, ?, ?, ?, ?)
722
+ ON CONFLICT(error_signature, module_name) DO UPDATE SET
723
+ symptom = excluded.symptom,
724
+ root_cause = excluded.root_cause,
725
+ groovy_patch = excluded.groovy_patch
726
+ """, (r["error_signature"], r["module_name"], r["symptom"], r["root_cause"], r["groovy_patch"], r["hit_count"]))
727
+ g_conn.commit()
728
+
729
+ # 2. Sync negative failed fixes
730
+ g_failed = g_conn.execute("SELECT * FROM failed_fixes").fetchall()
731
+ for f in g_failed:
732
+ l_conn.execute("""
733
+ INSERT OR IGNORE INTO failed_fixes (error_signature, attempted_patch, failure_reason, agent_role)
734
+ VALUES (?, ?, ?, ?)
735
+ """, (f["error_signature"], f["attempted_patch"], f["failure_reason"], f["agent_role"]))
736
+ l_conn.commit()
737
+
738
+ # 3. Sync lessons learned
739
+ g_lessons = g_conn.execute("SELECT * FROM lessons_learned").fetchall()
740
+ for r in g_lessons:
741
+ l_conn.execute("""
742
+ INSERT OR IGNORE INTO lessons_learned
743
+ (page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary, hit_count)
744
+ VALUES (?, ?, ?, ?, ?, ?, ?)
745
+ """, (r["page_context"], r["error_signature"], r["root_cause"], r["failed_attempt"], r["successful_fix"], r["lesson_summary"], r["hit_count"]))
746
+ l_conn.commit()
747
+
748
+ return len(g_rows)
754
749
 
755
750
 
756
751
  @retry_on_locked()
@@ -769,7 +764,7 @@ def record_lesson(page_context, error_signature, root_cause, failed_attempt, suc
769
764
  with get_db() as conn:
770
765
  cursor = conn.cursor()
771
766
  cursor.execute("""
772
- INSERT INTO lessons_learned
767
+ INSERT INTO lessons_learned
773
768
  (page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary)
774
769
  VALUES (?, ?, ?, ?, ?, ?)
775
770
  """, (page_context, error_signature, root_cause, failed_attempt, successful_fix, lesson_summary))
@@ -847,7 +842,7 @@ def get_flaky_telemetry(days=30, min_runs=2, threshold=0.15, target_db=None):
847
842
  with get_db(current_db) as conn:
848
843
  cursor = conn.cursor()
849
844
  cursor.execute("""
850
- SELECT
845
+ SELECT
851
846
  test_id,
852
847
  COUNT(*) as total_runs,
853
848
  SUM(CASE WHEN status = 'PASS' THEN 1 ELSE 0 END) as pass_count,
@@ -1051,7 +1046,7 @@ def main():
1051
1046
  if sol:
1052
1047
  print("==================================================")
1053
1048
  if sol.get("groovy_patch"):
1054
- print(f"✅ [CACHE HIT - 0 TOKENS] Found pre-tested solution!")
1049
+ print("✅ [CACHE HIT - 0 TOKENS] Found pre-tested solution!")
1055
1050
  print(f"Database Scope : {'LOCAL' if args.local else 'GLOBAL CENTRAL (~/.agent_memory)'}")
1056
1051
  print(f"Match Strategy : {sol.get('match_type', 'EXACT')}")
1057
1052
  print(f"Error Signature : {sol['error_signature']}")
@@ -1104,12 +1099,12 @@ def main():
1104
1099
  if not lessons:
1105
1100
  print(" (Zero relevant lessons in cache yet. Proceeding with clean reasoning.)")
1106
1101
  else:
1107
- for idx, l in enumerate(lessons, 1):
1108
- print(f"📌 [Lesson {idx}] Context: {l['page_context']} | Trigger: {l['error_signature']}")
1109
- print(f" 💡 Rule: {l['lesson_summary']}")
1110
- if l.get("failed_attempt") or l.get("successful_fix"):
1111
- print(f" ⚠️ Anti-Pattern: {l.get('failed_attempt') or 'N/A'}")
1112
- print(f" ✅ Proven Fix : {l.get('successful_fix') or 'N/A'}")
1102
+ for idx, les in enumerate(lessons, 1):
1103
+ print(f"📌 [Lesson {idx}] Context: {les['page_context']} | Trigger: {les['error_signature']}")
1104
+ print(f" 💡 Rule: {les['lesson_summary']}")
1105
+ if les.get("failed_attempt") or les.get("successful_fix"):
1106
+ print(f" ⚠️ Anti-Pattern: {les.get('failed_attempt') or 'N/A'}")
1107
+ print(f" ✅ Proven Fix : {les.get('successful_fix') or 'N/A'}")
1113
1108
  print("==================================================")
1114
1109
 
1115
1110
  elif args.command == "record-telemetry":
@@ -12,7 +12,6 @@ Autonomously detects Target Project or QA Hub (CLI, CWD, ENV).
12
12
 
13
13
  import os
14
14
  import sys
15
- import shutil
16
15
  import shlex
17
16
  import platform
18
17
  import subprocess
@@ -101,12 +100,14 @@ class DiagnosticRunner:
101
100
  return False, str(e)
102
101
 
103
102
  def check_os(self):
103
+ """Verifies operating system platform, kernel version, and architecture."""
104
104
  os_name = platform.system()
105
105
  os_ver = platform.release()
106
106
  arch = platform.machine()
107
107
  self.log_check("Operating System", "PASS", f"{os_name} {os_ver} ({arch})")
108
108
 
109
109
  def check_chrome(self):
110
+ """Checks for Google Chrome, Chromium, Brave, or Playwright browser runtimes."""
110
111
  # 1. Search via 'where' (Windows) or 'which' (Unix)
111
112
  cmd = "where chrome" if platform.system() == "Windows" else "which google-chrome google-chrome-stable chromium chromium-browser"
112
113
  found, out = self.run_command(cmd)
@@ -149,6 +150,7 @@ class DiagnosticRunner:
149
150
  "Download and install Google Chrome from https://www.google.com/chrome/")
150
151
 
151
152
  def check_java(self):
153
+ """Verifies presence of Java JRE/JDK runtime via CLI or JAVA_HOME."""
152
154
  found, out = self.run_command("java -version")
153
155
  if found or "version" in out:
154
156
  ver = out.splitlines()[0] if out else "Installed"
@@ -162,6 +164,7 @@ class DiagnosticRunner:
162
164
  "Katalon includes embedded JRE, but for Gradle/Jenkins CLI, install OpenJDK 17.")
163
165
 
164
166
  def check_katalon(self):
167
+ """Verifies Katalon Studio / Katalon Runtime Engine installation if applicable."""
165
168
  # Context-aware: check if current target is actually a Katalon project
166
169
  is_katalon_project = (
167
170
  any(self.target_dir.glob("*.prj"))
@@ -196,6 +199,7 @@ class DiagnosticRunner:
196
199
  self.log_check("Katalon Studio IDE", "PASS", "Not installed (Optional - not required for Playwright/Cypress/Pytest)")
197
200
 
198
201
  def check_node_allure(self):
202
+ """Verifies presence of Node.js runtime for test runners and Allure reports."""
199
203
  found_node, node_ver = self.run_command("node -v")
200
204
  if found_node:
201
205
  self.log_check("Node.js Runtime", "PASS", f"Node.js {node_ver}")
@@ -204,6 +208,7 @@ class DiagnosticRunner:
204
208
  "Install Node.js from https://nodejs.org/ to execute './tools/generate_allure_report.sh serve'")
205
209
 
206
210
  def check_python_runner(self):
211
+ """Verifies active Python environment and optional virtual environment presence."""
207
212
  py_ver = sys.version.split()[0]
208
213
  venv_path = self.target_dir / ".venv"
209
214
 
@@ -213,6 +218,7 @@ class DiagnosticRunner:
213
218
  self.log_check("Python Virtual Environment", "PASS", f"Python {py_ver} (Optional: create .venv if using auxiliary runners)")
214
219
 
215
220
  def check_project_structure(self):
221
+ """Audits folder layout and configuration integrity for Hub or Spoke projects."""
216
222
  # 1. QA Hub Repository Check
217
223
  is_hub = (self.target_dir / "skills_manifest.json").exists() or (self.target_dir / ".agents" / "skills").is_dir()
218
224
  if is_hub:
@@ -301,6 +307,7 @@ class DiagnosticRunner:
301
307
  "Run 'agent-qa setup .' to initialize Clean POM / Playwright configuration for this project.")
302
308
 
303
309
  def check_network(self):
310
+ """Audits AUT network reachability via BASE_URL configured in environment."""
304
311
  env_file = self.target_dir / ".env"
305
312
  if env_file.exists():
306
313
  try:
@@ -345,6 +352,7 @@ class DiagnosticRunner:
345
352
  "Check internet connection or enable VPN if corporate network required.")
346
353
 
347
354
  def run_all(self):
355
+ """Runs the complete diagnostic suite and returns 0 if all prerequisites pass."""
348
356
  print(f"\n{BOLD}{'='*70}{RESET}")
349
357
  print(f"{BOLD}{CYAN} ENTERPRISE AUTOMATION - ENVIRONMENT & ONBOARDING DIAGNOSTIC{RESET}")
350
358
  print(f"{BOLD}{'='*70}{RESET}")
@@ -111,6 +111,33 @@ def extract_versions(repo_root: str) -> Dict[str, str]:
111
111
  if m:
112
112
  versions["docs/ARCHITECTURE.md"] = m.group(1)
113
113
 
114
+ # 11. README.md
115
+ readme_path = os.path.join(repo_root, "README.md")
116
+ if os.path.exists(readme_path):
117
+ with open(readme_path, "r", encoding="utf-8") as f:
118
+ content = f.read()
119
+ m = re.search(r'rev:\s*v([0-9]+\.[0-9]+\.[0-9]+)', content)
120
+ if m:
121
+ versions["README.md"] = m.group(1)
122
+
123
+ # 12. README.en.md
124
+ readme_en_path = os.path.join(repo_root, "README.en.md")
125
+ if os.path.exists(readme_en_path):
126
+ with open(readme_en_path, "r", encoding="utf-8") as f:
127
+ content = f.read()
128
+ m = re.search(r'rev:\s*v([0-9]+\.[0-9]+\.[0-9]+)', content)
129
+ if m:
130
+ versions["README.en.md"] = m.group(1)
131
+
132
+ # 13. SECURITY.md
133
+ sec_path = os.path.join(repo_root, "SECURITY.md")
134
+ if os.path.exists(sec_path):
135
+ with open(sec_path, "r", encoding="utf-8") as f:
136
+ content = f.read()
137
+ m = re.search(r'\|\s*`([0-9]+\.[0-9]+\.x)`\s*\|\s*✅', content)
138
+ if m:
139
+ versions["SECURITY.md"] = m.group(1)
140
+
114
141
  return versions
115
142
 
116
143
 
@@ -127,7 +154,15 @@ def check_sync(repo_root: str, expected_version: Optional[str] = None) -> Tuple[
127
154
  if not target_ver:
128
155
  target_ver = next(iter(versions.values()))
129
156
 
130
- mismatches = {path: ver for path, ver in versions.items() if ver != target_ver}
157
+ target_prefix = ".".join(target_ver.split(".")[:2]) + ".x"
158
+ mismatches = {}
159
+ for path, ver in versions.items():
160
+ if path == "SECURITY.md":
161
+ if ver != target_prefix:
162
+ mismatches[path] = ver
163
+ elif ver != target_ver:
164
+ mismatches[path] = ver
165
+
131
166
  if mismatches:
132
167
  return False, versions, target_ver
133
168
  return True, versions, target_ver
@@ -151,10 +186,12 @@ def main() -> int:
151
186
  print(f"Target Version: v{canonical}")
152
187
  print("----------------------------------------------------------------------")
153
188
 
189
+ target_prefix = ".".join(canonical.split(".")[:2]) + ".x"
154
190
  all_matched = True
155
191
  for file_path, ver in sorted(versions.items()):
156
- status = "[MATCH]" if ver == canonical else "[MISMATCH]"
157
- if ver != canonical:
192
+ is_match = (ver == target_prefix) if file_path == "SECURITY.md" else (ver == canonical)
193
+ status = "[MATCH]" if is_match else "[MISMATCH]"
194
+ if not is_match:
158
195
  all_matched = False
159
196
  print(f" {status:<11} {file_path:<26} -> {ver}")
160
197
 
@@ -10,10 +10,8 @@ When a test fails due to a broken locator:
10
10
  """
11
11
 
12
12
  import os
13
- import sys
14
- import json
15
13
  import argparse
16
- from typing import Dict, Any, Optional
14
+ from typing import Dict, Any
17
15
  from datetime import datetime
18
16
 
19
17
 
@@ -35,11 +33,11 @@ class CIHealingHook:
35
33
  """
36
34
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
37
35
  branch_name = f"auto-heal/{test_id.lower()}_{timestamp}"
38
-
36
+
39
37
  pr_description = f"""## 🤖 Autonomous CI/CD Self-Healing Report
40
38
 
41
- > **Target File:** `{file_path}`
42
- > **Confidence Score:** `{confidence * 100:.1f}%`
39
+ > **Target File:** `{file_path}`
40
+ > **Confidence Score:** `{confidence * 100:.1f}%`
43
41
  > **Timestamp:** `{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}`
44
42
 
45
43
  ### 📝 Summary of Changes
@@ -54,7 +52,7 @@ The Self-Healing Engine successfully located the replacement element:
54
52
  ### ✅ Verification Status
55
53
  - [x] In-place AST replacement verified
56
54
  - [x] Syntax checked across target framework
57
- - [x] DoD 9 Quality Gates checked
55
+ - [x] DoD 10 Quality Gates checked
58
56
 
59
57
  ### 🚀 QA Action Required
60
58
  Review the diff above and **Merge PR** to lock the healed locator into main branch.
package/tools/cli.py CHANGED
@@ -41,7 +41,7 @@ RESET = "\033[0m"
41
41
  def cmd_version(args):
42
42
  """Displays the current hub version, skill catalog count, and license information."""
43
43
  manifest_path = os.path.join(REPO_ROOT, "skills_manifest.json")
44
- version = "2.6.0"
44
+ version = "3.0.0"
45
45
  if os.path.exists(manifest_path):
46
46
  try:
47
47
  import json
@@ -145,7 +145,7 @@ def cmd_clean(args):
145
145
  os.remove(os.path.join(root, f))
146
146
  removed_count += 1
147
147
  except OSError as err:
148
- failed_files = getattr(args, "verbose", False) and print(f"Notice: Could not remove {f}: {err}")
148
+ getattr(args, "verbose", False) and print(f"Notice: Could not remove {f}: {err}")
149
149
  print(f"✅ Cleanup finished: {removed_count} build artifacts and caches removed.")
150
150
 
151
151
 
@@ -255,7 +255,6 @@ def cmd_transpile(args):
255
255
  def cmd_omni_scan(args):
256
256
  from omni_transpiler import scan_repository
257
257
  inv = scan_repository(args.path)
258
- import json
259
258
  if getattr(args, "json", False):
260
259
  print(json.dumps(inv, indent=2))
261
260
  else:
@@ -542,21 +541,20 @@ def cmd_release(args):
542
541
 
543
542
  def _hub_version():
544
543
  manifest_path = os.path.join(REPO_ROOT, "skills_manifest.json")
545
- version = "2.6.0"
544
+ version = "3.0.0"
546
545
  if os.path.exists(manifest_path):
547
546
  try:
548
547
  import json
549
548
  with open(manifest_path, "r", encoding="utf-8") as f:
550
549
  version = json.load(f).get("version", version)
551
550
  except (OSError, json.JSONDecodeError):
552
- version = "2.6.0"
551
+ version = "3.0.0"
553
552
  return version
554
553
 
555
554
 
556
555
  class SynapseArgumentParser(argparse.ArgumentParser):
557
556
  """Enhanced ArgumentParser providing fuzzy Did-You-Mean suggestions on command typos."""
558
557
  def error(self, message):
559
- import difflib
560
558
  m = re.search(r"invalid choice: '([^']+)'", message)
561
559
  if m:
562
560
  bad_choice = m.group(1)
@@ -1008,7 +1006,7 @@ def main():
1008
1006
  import update_sentinel
1009
1007
  update_sentinel.check_for_updates(force=False, notify=True)
1010
1008
  except (ImportError, OSError, TimeoutError):
1011
- update_status = None
1009
+ _ = False
1012
1010
 
1013
1011
  # Dispatch subcommand
1014
1012
  args.func(args)