synapse-hub 2.7.0 → 3.0.1

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 (141) hide show
  1. package/.agents/agents/live-explorer.md +2 -0
  2. package/.agents/agents/pom-crafter.md +2 -0
  3. package/.agents/agents/test-runner.md +3 -0
  4. package/.agents/rules/communication-style.md +4 -0
  5. package/.agents/rules/context-engineering.md +1 -0
  6. package/.agents/rules/git-push-protocol.md +17 -0
  7. package/.agents/skills/agentless-qa-repair/examples/three_phase_repair_workflow.md +40 -0
  8. package/.agents/skills/antigravity-test-sentinel/examples/sprint_dod_enforcement.md +47 -0
  9. package/.agents/skills/auth-session-and-cookie-manager/examples/multi_role_auth_matrix.md +70 -0
  10. package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_playwright_pom.ts +52 -0
  11. package/.agents/skills/failure-autopsy-and-healing/examples/closed_loop_healing_walkthrough.md +48 -0
  12. package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
  13. package/.agents/skills/lean-impact-scoreboard/examples/sample_scoreboard.md +1 -1
  14. package/.agents/skills/mipro-prompt-compiler/examples/dspy_assertion_optimizer_case.md +46 -0
  15. package/.agents/skills/persistent-planning-files/SKILL.md +1 -1
  16. package/.agents/skills/qa-aci-interface/examples/surgical_code_window_walkthrough.md +40 -0
  17. package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
  18. package/.agents/skills/review-spec/SKILL.md +1 -1
  19. package/.agents/skills/review-spec/examples/pocock_grilling_decision_rubric.md +40 -0
  20. package/.agents/skills/senior-code-review/SKILL.md +1 -1
  21. package/.agents/skills/skillspector-security-guard/examples/audit_threat_vectors_report.json +35 -0
  22. package/.agents/skills/surgical-coding-principles/SKILL.md +1 -1
  23. package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
  24. package/.agents/skills/tot-exploratory-testing/examples/payment_gateway_tot_tree.json +62 -0
  25. package/.agents/skills/verification-before-completion/SKILL.md +2 -2
  26. package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +2 -2
  27. package/.agents/skills/web-agent-gym-benchmarking/examples/enterprise_tasks_suite.json +38 -0
  28. package/.agents/skills/web-vitals-and-performance-profiler/examples/playwright_cwv_profiler_fixture.ts +61 -0
  29. package/AGENTS.md +6 -4
  30. package/CLAUDE.md +2 -1
  31. package/README.en.md +71 -292
  32. package/README.md +69 -304
  33. package/action.yml +1 -1
  34. package/bin/agent-qa.js +2 -2
  35. package/docs/ARCHITECTURE.en.md +192 -0
  36. package/docs/ARCHITECTURE.md +135 -88
  37. package/docs/CI_CD_INTEGRATION.en.md +96 -0
  38. package/docs/CI_CD_INTEGRATION.md +24 -22
  39. package/docs/CLI_REFERENCE.en.md +174 -0
  40. package/docs/CLI_REFERENCE.md +120 -115
  41. package/docs/CONTRIBUTING.en.md +49 -0
  42. package/docs/CONTRIBUTING.md +26 -68
  43. package/docs/QUALITY_GATES.en.md +115 -0
  44. package/docs/QUALITY_GATES.md +115 -0
  45. package/docs/research/agent_skill_training_analysis.md +1 -1
  46. package/package.json +1 -1
  47. package/scripts/package_dist.py +2 -1
  48. package/skills_manifest.json +6 -6
  49. package/synapse.config.json +1 -1
  50. package/templates/ai_bridges/CLAUDE.md +2 -1
  51. package/templates/ai_bridges/copilot-instructions.md +2 -2
  52. package/tools/__init__.py +18 -1
  53. package/tools/_openapi_parser.py +1 -1
  54. package/tools/agent_canary.py +42 -30
  55. package/tools/agentless_patcher.py +2 -2
  56. package/tools/analyze_project.py +3 -5
  57. package/tools/api_drift_sentinel.py +4 -4
  58. package/tools/api_fuzzer.py +8 -8
  59. package/tools/auto_heal.py +4 -5
  60. package/tools/auto_heal_reflexion.py +10 -2
  61. package/tools/autogen_qa_broker.py +2 -2
  62. package/tools/browser_helpers.js +2 -2
  63. package/tools/browsergym_eval.py +2 -3
  64. package/tools/bug_reporter.py +11 -13
  65. package/tools/bump_version.py +90 -19
  66. package/tools/cache_resolver.py +78 -83
  67. package/tools/check_env.py +9 -1
  68. package/tools/check_version_sync.py +58 -4
  69. package/tools/ci_healing_hook.py +4 -6
  70. package/tools/cli.py +5 -7
  71. package/tools/config_decoupler.py +2 -3
  72. package/tools/contract_validator.py +141 -0
  73. package/tools/core/__init__.py +52 -0
  74. package/tools/count_tokens.py +22 -20
  75. package/tools/dashboard_generator.py +629 -1000
  76. package/tools/db_sandbox.py +87 -62
  77. package/tools/debt_harvester.py +3 -3
  78. package/tools/distill_log.py +3 -3
  79. package/tools/dspy_qa_signatures.py +3 -4
  80. package/tools/export_postman_collection.py +8 -8
  81. package/tools/flaky_detector.py +2 -2
  82. package/tools/fuzz_payloads.py +5 -5
  83. package/tools/generate_allure_report.py +1 -1
  84. package/tools/generate_jmx_plan.py +4 -4
  85. package/tools/gherkin_suite.py +1 -1
  86. package/tools/git_pre_commit_guard.py +2 -2
  87. package/tools/goldfish_review.py +2 -2
  88. package/tools/heal/__init__.py +28 -0
  89. package/tools/heal_pr_automator.py +6 -6
  90. package/tools/heuristic_locator_matcher.py +2 -2
  91. package/tools/infer_linter.py +143 -29
  92. package/tools/interview_story.py +0 -1
  93. package/tools/jtl_analyzer.py +11 -11
  94. package/tools/lint/__init__.py +30 -0
  95. package/tools/lint_ai_docs.py +42 -0
  96. package/tools/lookup_tc.py +1 -1
  97. package/tools/matrix_sharder.py +15 -13
  98. package/tools/mcp/__init__.py +16 -0
  99. package/tools/mcp_excel_server.py +2 -2
  100. package/tools/mcp_server.py +9 -7
  101. package/tools/md_to_xlsx.py +26 -26
  102. package/tools/micro_mutator.py +32 -15
  103. package/tools/network_mock_shield.py +27 -27
  104. package/tools/newman_analyzer.py +2 -3
  105. package/tools/omni_emitter.py +5 -8
  106. package/tools/omni_parser.py +5 -5
  107. package/tools/omni_transpiler.py +5 -5
  108. package/tools/output_formatter.py +19 -0
  109. package/tools/perf/__init__.py +24 -0
  110. package/tools/pinpoint_failure.py +1 -1
  111. package/tools/project_state.py +0 -2
  112. package/tools/qa_event_stream.py +1 -1
  113. package/tools/repo_map.py +8 -8
  114. package/tools/report/__init__.py +32 -0
  115. package/tools/reversible_sandbox.py +3 -4
  116. package/tools/safety_firewall.py +2 -2
  117. package/tools/senior_code_review.py +4 -3
  118. package/tools/setup_project.py +10 -12
  119. package/tools/skill_collision_guard.py +3 -3
  120. package/tools/skill_vault.py +94 -6
  121. package/tools/skillspector.py +10 -8
  122. package/tools/smart_visual_masker.py +4 -6
  123. package/tools/spec_drift_sentinel.py +1 -2
  124. package/tools/swe_qa_aci.py +2 -2
  125. package/tools/synapse_qa_bench.py +3 -3
  126. package/tools/sync_agent_rules.py +18 -3
  127. package/tools/telemetry_correlator.py +3 -2
  128. package/tools/test_impact.py +2 -1
  129. package/tools/test_memory_os.py +15 -5
  130. package/tools/textgrad_mutation_optimizer.py +3 -3
  131. package/tools/tms_sync_bridge.py +5 -5
  132. package/tools/tot_exploratory_tester.py +5 -6
  133. package/tools/traffic_session_converter.py +9 -9
  134. package/tools/universal_ir.py +2 -3
  135. package/tools/update_sentinel.py +6 -9
  136. package/tools/verify_all.py +12 -2
  137. package/tools/visual_diff_canvas.py +4 -4
  138. package/tools/visual_som_helper.py +15 -13
  139. package/tools/watch_mode.py +1 -1
  140. package/tools/wcag_audit.py +22 -1
  141. package/tools/welcome_page.py +5 -6
@@ -34,7 +34,7 @@ import sqlite3
34
34
  import argparse
35
35
  import urllib.request
36
36
  import urllib.error
37
- from typing import Dict, Any, List, Optional, Tuple
37
+ from typing import Dict, Any, List, Tuple
38
38
 
39
39
  # Enforce UTF-8 stdout encoding for Windows console compatibility
40
40
  if hasattr(sys.stdout, 'reconfigure'):
@@ -86,18 +86,24 @@ class DBSandbox:
86
86
 
87
87
  if not os.path.exists(self.db_path):
88
88
  conn = sqlite3.connect(self.db_path)
89
- cursor = conn.cursor()
90
- cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, code TEXT, status TEXT)")
91
- cursor.execute("CREATE TABLE IF NOT EXISTS orders (id INTEGER PRIMARY KEY, user_id INTEGER, amount REAL)")
92
- conn.commit()
93
- conn.close()
89
+ try:
90
+ cursor = conn.cursor()
91
+ cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, code TEXT, status TEXT)")
92
+ cursor.execute("CREATE TABLE IF NOT EXISTS orders (id INTEGER PRIMARY KEY, user_id INTEGER, amount REAL)")
93
+ conn.commit()
94
+ finally:
95
+ conn.close()
94
96
 
95
97
  # Perform atomic SQLite backup
96
98
  src_conn = sqlite3.connect(self.db_path)
97
- dst_conn = sqlite3.connect(snapshot_file)
98
- src_conn.backup(dst_conn)
99
- src_conn.close()
100
- dst_conn.close()
99
+ try:
100
+ dst_conn = sqlite3.connect(snapshot_file)
101
+ try:
102
+ src_conn.backup(dst_conn)
103
+ finally:
104
+ dst_conn.close()
105
+ finally:
106
+ src_conn.close()
101
107
 
102
108
  else:
103
109
  # PostgreSQL / MySQL Docker hook or file copy
@@ -150,10 +156,14 @@ class DBSandbox:
150
156
 
151
157
  if self.db_type == "sqlite":
152
158
  src_conn = sqlite3.connect(snapshot_file)
153
- dst_conn = sqlite3.connect(self.db_path)
154
- src_conn.backup(dst_conn)
155
- src_conn.close()
156
- dst_conn.close()
159
+ try:
160
+ dst_conn = sqlite3.connect(self.db_path)
161
+ try:
162
+ src_conn.backup(dst_conn)
163
+ finally:
164
+ dst_conn.close()
165
+ finally:
166
+ src_conn.close()
157
167
  else:
158
168
  shutil.copy2(snapshot_file, self.db_path)
159
169
 
@@ -181,25 +191,37 @@ class DBSandbox:
181
191
  cursor = conn.cursor()
182
192
  executed_count = 0
183
193
 
194
+ safe_sp = re.sub(r'[^a-zA-Z0-9_]', '', savepoint_name) or "sp_test_run"
184
195
  try:
185
- cursor.execute(f"SAVEPOINT {savepoint_name};")
196
+ cursor.execute(f"SAVEPOINT {safe_sp};")
186
197
  for sql in sql_commands:
187
198
  cursor.execute(sql)
188
199
  executed_count += 1
189
200
 
190
- # Simulate verification read inside the savepoint
191
- cursor.execute("SELECT COUNT(*) FROM users")
192
- count_inside_tx = cursor.fetchone()[0]
201
+ # Determine sample table for verification read (prefer 'users', fallback to first user table)
202
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
203
+ tables = [row[0] for row in cursor.fetchall()]
204
+ target_table = "users" if "users" in tables else (tables[0] if tables else None)
205
+
206
+ count_inside_tx = 0
207
+ if target_table:
208
+ safe_tbl = target_table.replace('"', '""')
209
+ cursor.execute(f'SELECT COUNT(*) FROM "{safe_tbl}"')
210
+ count_inside_tx = cursor.fetchone()[0]
211
+ else:
212
+ count_inside_tx = executed_count
193
213
 
194
214
  # Enforce automatic zero-pollution rollback
195
- cursor.execute(f"ROLLBACK TO SAVEPOINT {savepoint_name};")
196
- cursor.execute(f"RELEASE SAVEPOINT {savepoint_name};")
215
+ cursor.execute(f"ROLLBACK TO SAVEPOINT {safe_sp};")
216
+ cursor.execute(f"RELEASE SAVEPOINT {safe_sp};")
197
217
 
198
218
  # Verify that outside the savepoint, table is pristine
199
- cursor.execute("SELECT COUNT(*) FROM users")
200
- count_after_rollback = cursor.fetchone()[0]
219
+ count_after_rollback = 0
220
+ if target_table:
221
+ safe_tbl = target_table.replace('"', '""')
222
+ cursor.execute(f'SELECT COUNT(*) FROM "{safe_tbl}"')
223
+ count_after_rollback = cursor.fetchone()[0]
201
224
 
202
- conn.close()
203
225
  return {
204
226
  "status": "SUCCESS",
205
227
  "statements_executed": executed_count,
@@ -210,8 +232,7 @@ class DBSandbox:
210
232
  }
211
233
  except Exception as e:
212
234
  try:
213
- cursor.execute(f"ROLLBACK TO SAVEPOINT {savepoint_name};")
214
- conn.close()
235
+ cursor.execute(f"ROLLBACK TO SAVEPOINT {safe_sp};")
215
236
  except sqlite3.Error as rollback_err:
216
237
  sys.stderr.write(f"Notice: Savepoint rollback notice: {rollback_err}\n")
217
238
  return {
@@ -219,6 +240,8 @@ class DBSandbox:
219
240
  "error": str(e),
220
241
  "zero_pollution_verified": True
221
242
  }
243
+ finally:
244
+ conn.close()
222
245
 
223
246
  # -------------------------------------------------------------------------
224
247
  # 3. TAINT POLLUTION AUDIT & PURGE
@@ -254,43 +277,45 @@ class DBSandbox:
254
277
  }
255
278
 
256
279
  conn = sqlite3.connect(self.db_path)
257
- cursor = conn.cursor()
258
- cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
259
- tables = [r[0] for r in cursor.fetchall()]
260
-
261
- total_purged = 0
262
- purge_log = []
263
-
264
- for table in tables:
265
- # Validate table identifier to protect against injection
266
- if not re.match(r'^[a-zA-Z0-9_]+$', table):
267
- continue
268
- cursor.execute(f"PRAGMA table_info([{table}]);")
269
- columns = [c[1] for c in cursor.fetchall()]
270
-
271
- # Look for code or name columns
272
- target_cols = [c for c in columns if c.lower() in ("code", "username", "name", "email", "status") and re.match(r'^[a-zA-Z0-9_]+$', c)]
273
- for col in target_cols:
274
- for prefix in known_test_prefixes:
275
- escaped_pattern = self._escape_sql_like(prefix) + "%"
276
- cursor.execute(
277
- f"SELECT COUNT(*) FROM [{table}] WHERE [{col}] LIKE ? ESCAPE '\\'",
278
- (escaped_pattern,)
279
- )
280
- c_count = cursor.fetchone()[0]
281
- if c_count > 0:
282
- if not dry_run:
283
- cursor.execute(
284
- f"DELETE FROM [{table}] WHERE [{col}] LIKE ? ESCAPE '\\'",
285
- (escaped_pattern,)
286
- )
287
- total_purged += c_count
288
- action_str = "previewed" if dry_run else "deleted"
289
- purge_log.append(f"Table '{table}' ({col}): {action_str} {c_count} rows matching literal prefix '{prefix}'")
290
-
291
- if not dry_run:
292
- conn.commit()
293
- conn.close()
280
+ try:
281
+ cursor = conn.cursor()
282
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
283
+ tables = [r[0] for r in cursor.fetchall()]
284
+
285
+ total_purged = 0
286
+ purge_log = []
287
+
288
+ for table in tables:
289
+ # Validate table identifier to protect against injection
290
+ if not re.match(r'^[a-zA-Z0-9_]+$', table):
291
+ continue
292
+ cursor.execute(f"PRAGMA table_info([{table}]);")
293
+ columns = [c[1] for c in cursor.fetchall()]
294
+
295
+ # Look for code or name columns
296
+ target_cols = [c for c in columns if c.lower() in ("code", "username", "name", "email", "status") and re.match(r'^[a-zA-Z0-9_]+$', c)]
297
+ for col in target_cols:
298
+ for prefix in known_test_prefixes:
299
+ escaped_pattern = self._escape_sql_like(prefix) + "%"
300
+ cursor.execute(
301
+ f"SELECT COUNT(*) FROM [{table}] WHERE [{col}] LIKE ? ESCAPE '\\'",
302
+ (escaped_pattern,)
303
+ )
304
+ c_count = cursor.fetchone()[0]
305
+ if c_count > 0:
306
+ if not dry_run:
307
+ cursor.execute(
308
+ f"DELETE FROM [{table}] WHERE [{col}] LIKE ? ESCAPE '\\'",
309
+ (escaped_pattern,)
310
+ )
311
+ total_purged += c_count
312
+ action_str = "previewed" if dry_run else "deleted"
313
+ purge_log.append(f"Table '{table}' ({col}): {action_str} {c_count} rows matching literal prefix '{prefix}'")
314
+
315
+ if not dry_run:
316
+ conn.commit()
317
+ finally:
318
+ conn.close()
294
319
 
295
320
  return {
296
321
  "status": "CLEAN" if not dry_run else "DRY_RUN",
@@ -67,9 +67,9 @@ class ShortcutEntry:
67
67
  # 1. shortcut: <desc>, ceiling: <limit>, upgrade: <trigger>
68
68
  # 2. shortcut: <desc>, <limit>, <trigger>
69
69
  # 3. shortcut: <limit>, <when to upgrade>
70
-
70
+
71
71
  parts = [p.strip() for p in text.split(',') if p.strip()]
72
-
72
+
73
73
  # Check explicit labels
74
74
  ceiling_match = re.search(r'(?:ceiling|limit):\s*([^,]+)', text, re.IGNORECASE)
75
75
  upgrade_match = re.search(r'(?:upgrade|when|trigger):\s*([^,]+)', text, re.IGNORECASE)
@@ -153,7 +153,7 @@ def format_markdown(entries: List[ShortcutEntry], target_dir: str) -> str:
153
153
  """Renders shortcut debt entries into a structured Markdown ledger."""
154
154
  total = len(entries)
155
155
  no_trigger = sum(1 for e in entries if not e.has_trigger)
156
-
156
+
157
157
  lines = [
158
158
  "# Technical Debt & Shortcut Ledger",
159
159
  "",
@@ -22,7 +22,7 @@ if hasattr(sys.stdout, 'reconfigure'):
22
22
  def distill(log_text, max_lines=40):
23
23
  lines = log_text.splitlines()
24
24
  total_raw_lines = len(lines)
25
-
25
+
26
26
  # Noise filters
27
27
  noise_patterns = [
28
28
  re.compile(r'^\s*$'),
@@ -54,7 +54,7 @@ def distill(log_text, max_lines=40):
54
54
 
55
55
  tc_pattern = re.compile(r'(TC-\d+|test_[A-Za-z0-9_]+|[A-Za-z0-9_-]+\.(?:spec|test)\.[jt]sx?)')
56
56
  line_num_pattern = re.compile(r'((?:Script\d+|\w+Page|\w+\.(?:groovy|ts|js|py)):(\d+))|File "([^"]+)", line (\d+)')
57
- exception_pattern = re.compile(r'([A-Za-z0-9_.]*(?:Exception|Error)):?\s*(.*)')
57
+ re.compile(r'([A-Za-z0-9_.]*(?:Exception|Error)):?\s*(.*)')
58
58
 
59
59
  captured_failures = []
60
60
  failing_tcs = set()
@@ -110,7 +110,7 @@ def distill(log_text, max_lines=40):
110
110
  output.append(f" Targeted TCs: {', '.join(sorted(failing_tcs))}")
111
111
  else:
112
112
  output.append(f"❌ FAILURES / ERRORS DETECTED: {len(captured_failures)} failure block(s) isolated.")
113
-
113
+
114
114
  if failing_locs:
115
115
  unique_locs = sorted(set(failing_locs))
116
116
  output.append(f" Failing Script Location(s): {', '.join(unique_locs[:5])}")
@@ -15,12 +15,11 @@ Reference:
15
15
  - Synapse-Hub Subsystem 3: Section 6.4 of agent_skill_training_analysis.md.
16
16
  """
17
17
 
18
- import os
19
18
  import sys
20
19
  import json
21
20
  import re
22
21
  import argparse
23
- from typing import Dict, Any, Optional, Tuple, List
22
+ from typing import Dict, Any, Optional, Tuple
24
23
 
25
24
  # Enforce UTF-8 stdout encoding for Windows console compatibility
26
25
  if hasattr(sys.stdout, 'reconfigure'):
@@ -261,7 +260,7 @@ def qa_dod_metric(gold: Any, pred: Any, trace: Optional[Any] = None) -> float:
261
260
  Returns 1.0 (PASS) or 0.0 (FAIL).
262
261
  """
263
262
  test_code = getattr(pred, "test_script_code", "")
264
- page_code = getattr(pred, "page_class_code", "")
263
+ getattr(pred, "page_class_code", "")
265
264
 
266
265
  if not test_code:
267
266
  return 0.0
@@ -305,7 +304,7 @@ def main():
305
304
  return 0
306
305
 
307
306
  if args.rca:
308
- print(f"\n🩺 Diagnosing Failure Trace with RCA Signature...")
307
+ print("\n🩺 Diagnosing Failure Trace with RCA Signature...")
309
308
  diagnosis = OfflineSignatureCompiler.diagnose_rca_failure(args.rca)
310
309
  print(json.dumps(diagnosis, indent=2))
311
310
  return 0
@@ -41,7 +41,7 @@ import uuid
41
41
  import csv
42
42
  import argparse
43
43
  from typing import Dict, Any, List, Optional
44
- from urllib.parse import urlparse, parse_qs
44
+ from urllib.parse import urlparse
45
45
 
46
46
  # Enforce UTF-8 stdout encoding for Windows/macOS/Linux console compatibility
47
47
  if hasattr(sys.stdout, "reconfigure"):
@@ -151,7 +151,7 @@ def mock_schema_payload(schema: Dict[str, Any], depth: int = 0) -> Any:
151
151
  return "{{$guid}}"
152
152
  elif fmt in ("date-time", "date"):
153
153
  return "{{$isoTimestamp}}"
154
- return schema.get("example") or f"AUTO_{{{{$randomAlphaNumeric}}}}"
154
+ return schema.get("example") or "AUTO_{{$randomAlphaNumeric}}"
155
155
 
156
156
  elif stype in ("integer", "number"):
157
157
  return schema.get("example") or schema.get("minimum", 100)
@@ -191,7 +191,7 @@ def parse_openapi_spec(spec_data: Dict[str, Any], base_url_override: Optional[st
191
191
  base_url_default = "https://api.staging.enterprise.local"
192
192
 
193
193
  # Postman root structure
194
- collection = {
194
+ collection: Dict[str, Any] = {
195
195
  "info": {
196
196
  "_postman_id": generate_uuid(),
197
197
  "name": col_name,
@@ -302,7 +302,7 @@ def parse_openapi_spec(spec_data: Dict[str, Any], base_url_override: Optional[st
302
302
  else:
303
303
  normalized_segments.append(seg)
304
304
 
305
- url_obj = {
305
+ url_obj: Dict[str, Any] = {
306
306
  "raw": f"{{{{baseUrl}}}}{raw_path}",
307
307
  "host": ["{{baseUrl}}"],
308
308
  "path": normalized_segments
@@ -325,7 +325,7 @@ def parse_openapi_spec(spec_data: Dict[str, Any], base_url_override: Optional[st
325
325
  if not resp_schema and "schema" in resp_info:
326
326
  resp_schema = resp_info["schema"]
327
327
 
328
- postman_request = {
328
+ postman_request: Dict[str, Any] = {
329
329
  "name": summary,
330
330
  "event": [
331
331
  {
@@ -380,7 +380,7 @@ def parse_har_file(har_data: Dict[str, Any], base_url_override: Optional[str] =
380
380
 
381
381
  base_url_default = base_url_override or "https://api.staging.enterprise.local"
382
382
 
383
- collection = {
383
+ collection: Dict[str, Any] = {
384
384
  "info": {
385
385
  "_postman_id": generate_uuid(),
386
386
  "name": collection_name,
@@ -428,7 +428,7 @@ def parse_har_file(har_data: Dict[str, Any], base_url_override: Optional[str] =
428
428
  }
429
429
 
430
430
  expected_status = resp.get("status", 200) or 200
431
- postman_item = {
431
+ postman_item: Dict[str, Any] = {
432
432
  "name": f"[{method}] {p.path or '/'}",
433
433
  "event": [
434
434
  {
@@ -918,7 +918,7 @@ def main():
918
918
  with open(target_output, "w", encoding="utf-8") as f:
919
919
  json.dump(collection, f, indent=2, ensure_ascii=False)
920
920
  print(f"✅ [POSTMAN EXPORT] Successfully created Postman Collection v2.1.0: {target_output}")
921
- print(f"🛡️ Embedded: Pre-request Nonce & Auth | SLA Tests (< 1000ms) | AJV JSON Schema Validation | Token Chaining")
921
+ print("🛡️ Embedded: Pre-request Nonce & Auth | SLA Tests (< 1000ms) | AJV JSON Schema Validation | Token Chaining")
922
922
 
923
923
  # Optional Companion Environment
924
924
  env_file = args.env_output or (os.path.splitext(target_output)[0] + "_env.json")
@@ -213,7 +213,7 @@ def benchmark_live_command(cmd: str, iterations: int = 5, test_id: Optional[str]
213
213
  print(f" Recommendation : {RED}{BOLD}[QUARANTINE REQUIRED]{RESET} Tag with @quarantine immediately!")
214
214
  else:
215
215
  print(f" Recommendation : {GREEN}[SUITE APPROVED]{RESET} Test is deterministic.")
216
- print(f"======================================================================\n")
216
+ print("======================================================================\n")
217
217
 
218
218
  # Persist Report
219
219
  os.makedirs(REPORTS_DIR, exist_ok=True)
@@ -334,7 +334,7 @@ def main():
334
334
  if args.isolate:
335
335
  auto_quarantine_test_file(c["test_id"], target_dir=args.target_dir)
336
336
 
337
- print(f"======================================================================")
337
+ print("======================================================================")
338
338
  print(f"Quarantine Recommendations: {quarantined_count}/{len(candidates)}")
339
339
  sys.exit(0)
340
340
 
@@ -164,9 +164,9 @@ def main():
164
164
  if args.json:
165
165
  print(json.dumps(scenarios, indent=2, ensure_ascii=False))
166
166
  else:
167
- print(f"\n======================================================================")
167
+ print("\n======================================================================")
168
168
  print(f" EDGE-CASE FUZZING SCENARIOS FOR FIELD: '{args.field}' ({args.type.upper()})")
169
- print(f"======================================================================")
169
+ print("======================================================================")
170
170
  for idx, sc in enumerate(scenarios, start=1):
171
171
  val_disp = repr(sc['value']) if len(str(sc['value'])) < 60 else repr(str(sc['value'])[:50] + "...")
172
172
  print(f" [{idx:02d}] {sc['scenario']:<45} | Expected: HTTP {sc['expected']} | Value: {val_disp}")
@@ -177,9 +177,9 @@ def main():
177
177
  if args.json:
178
178
  print(json.dumps(catalog, indent=2, ensure_ascii=False))
179
179
  else:
180
- print(f"\n======================================================================")
181
- print(f" ENTERPRISE EDGE-CASE FUZZING & SANITIZATION CATALOG ")
182
- print(f"======================================================================")
180
+ print("\n======================================================================")
181
+ print(" ENTERPRISE EDGE-CASE FUZZING & SANITIZATION CATALOG ")
182
+ print("======================================================================")
183
183
  total_payloads = sum(len(items) for items in catalog.values())
184
184
  print(f"Categories: {len(catalog)} | Total Payloads: {total_payloads}\n")
185
185
 
@@ -41,7 +41,7 @@ def generate_allure_report(results_dir=None, report_dir=None, clean=True, open_b
41
41
 
42
42
  print(f"[+] Generating Allure HTML report from '{results_path}' to '{report_path}'...")
43
43
  try:
44
- ret = subprocess.run(cmd, check=True)
44
+ subprocess.run(cmd, check=True, timeout=120.0)
45
45
  print(f"[+] Report generated successfully at: {report_path}")
46
46
  except subprocess.CalledProcessError as e:
47
47
  print(f"[ERROR] Allure report generation failed with exit code {e.returncode}")
@@ -125,7 +125,7 @@ def build_jmx_xml(
125
125
  kafka_config: Optional[Dict[str, Any]] = None
126
126
  ) -> str:
127
127
  """Builds valid, production-grade Apache JMeter 5.x XML Test Plan."""
128
-
128
+
129
129
  loops_str = "-1" if loop_count < 0 else str(loop_count)
130
130
  continue_forever = "true" if loop_count < 0 else "false"
131
131
 
@@ -960,7 +960,7 @@ def parse_openapi_for_jmeter(spec_data: Dict[str, Any]) -> tuple:
960
960
  for p_name, p_schema in props.items():
961
961
  ptype = p_schema.get("type", "string")
962
962
  if ptype == "string":
963
- sample_payload[p_name] = p_schema.get("example") or f"PERF_AUTO_${{__time(HMS,)}}"
963
+ sample_payload[p_name] = p_schema.get("example") or "PERF_AUTO_${__time(HMS,)}"
964
964
  elif ptype in ("integer", "number"):
965
965
  sample_payload[p_name] = p_schema.get("example") or 100
966
966
  elif ptype == "boolean":
@@ -1075,7 +1075,7 @@ def parse_har_for_jmeter(har_data: Dict[str, Any]) -> tuple:
1075
1075
  url = req.get("url", "/")
1076
1076
  p = urlparse(url)
1077
1077
  method = req.get("method", "GET").upper()
1078
-
1078
+
1079
1079
  post_data = req.get("postData", {})
1080
1080
  body_str = post_data.get("text", "")
1081
1081
  status_code = e.get("response", {}).get("status", 200)
@@ -1107,7 +1107,7 @@ def main():
1107
1107
  parser.add_argument("--output", "-o", help="Target output JMeter test plan file path (.jmx).")
1108
1108
  parser.add_argument("--emit-locust", dest="emit_locust", help="Also/instead emit a runnable Locust locustfile.py (code-as-code load model).")
1109
1109
  parser.add_argument("--sample", action="store_true", help="Generate a sample load test plan using built-in enterprise schema.")
1110
-
1110
+
1111
1111
  # Advanced JMeter 360° Extensions
1112
1112
  parser.add_argument("--no-cookies", action="store_true", help="Disable automatic HTTP Cookie Manager.")
1113
1113
  parser.add_argument("--no-cache", action="store_true", help="Disable automatic HTTP Cache Manager.")
@@ -649,7 +649,7 @@ def compile_feature(path: str) -> Dict[str, Any]:
649
649
  errors = list(parser.errors)
650
650
 
651
651
  cases = expand_feature(feature, errors)
652
- error_count = sum(len(c["errors"]) for c in cases) + len(errors)
652
+ sum(len(c["errors"]) for c in cases) + len(errors)
653
653
  gaps = analyze_gaps(feature, cases)
654
654
 
655
655
  mapped = {c["id"]: [map_step_to_ir(s).to_dict() for s in c["steps"]] for c in cases}
@@ -94,7 +94,7 @@ def main():
94
94
  if not git_dir or not os.path.isdir(git_dir):
95
95
  print("❌ Error: Not a git repository.")
96
96
  sys.exit(1)
97
-
97
+
98
98
  # 1. Pre-commit hook
99
99
  pre_commit_path = os.path.join(git_dir, "hooks", "pre-commit")
100
100
  pre_commit_content = (
@@ -134,7 +134,7 @@ def main():
134
134
  fp.write(pre_push_content)
135
135
  os.chmod(pre_push_path, 0o755)
136
136
 
137
- print(f"✅ Git hooks installed successfully:")
137
+ print("✅ Git hooks installed successfully:")
138
138
  print(f" - Pre-commit: {pre_commit_path}")
139
139
  print(f" - Post-merge: {post_merge_path} (Auto-runs cache_resolver.py init on git pull)")
140
140
  print(f" - Pre-push: {pre_push_path} (Auto-runs cache_resolver.py sync on git push)")
@@ -108,8 +108,8 @@ def evaluate_spec_content(content: str, filename: str = "") -> dict:
108
108
  findings = []
109
109
  vector_scores = {}
110
110
 
111
- lines = [line.strip() for line in content.splitlines() if line.strip()]
112
- full_text = content.lower()
111
+ [line.strip() for line in content.splitlines() if line.strip()]
112
+ content.lower()
113
113
 
114
114
  # -------------------------------------------------------------
115
115
  # Vector 1: Comprehension & System Boundaries (Weight: 20)
@@ -0,0 +1,28 @@
1
+ """
2
+ Synapse Hub — Self-Healing & Autonomous Repair Engines (tools.heal)
3
+ -------------------------------------------------------------------
4
+ Domain package grouping all locator repair, AOM semantic diffing,
5
+ episodic Reflexion memory, and agentless patching tools.
6
+ """
7
+
8
+ from tools import (
9
+ agentless_patcher,
10
+ auto_heal,
11
+ auto_heal_reflexion,
12
+ cache_resolver,
13
+ ci_healing_hook,
14
+ heal_pr_automator,
15
+ heuristic_locator_matcher,
16
+ reversible_sandbox,
17
+ )
18
+
19
+ __all__ = [
20
+ "agentless_patcher",
21
+ "auto_heal",
22
+ "auto_heal_reflexion",
23
+ "cache_resolver",
24
+ "ci_healing_hook",
25
+ "heal_pr_automator",
26
+ "heuristic_locator_matcher",
27
+ "reversible_sandbox",
28
+ ]
@@ -114,8 +114,8 @@ def generate_pr_draft(
114
114
 
115
115
  pr_body = fr"""### 🛡️ Automated Self-Healing Verification PR
116
116
 
117
- > **Origin:** Enterprise Autonomous QA Sentinel
118
- > **Confidence Rating:** `{confidence_info['confidence_percentage']}%` ({confidence_info['risk_tier']})
117
+ > **Origin:** Enterprise Autonomous QA Sentinel
118
+ > **Confidence Rating:** `{confidence_info['confidence_percentage']}%` ({confidence_info['risk_tier']})
119
119
  > **Zero Silent Drift Guarantee:** This change was isolated to prevent undocumented modifications on `main`.
120
120
 
121
121
  ---
@@ -183,9 +183,9 @@ def main():
183
183
  if args.json:
184
184
  print(json.dumps(res, indent=2))
185
185
  else:
186
- print(f"\n======================================================================")
186
+ print("\n======================================================================")
187
187
  print(f" CI/CD SILENT SELF-HEALING HOOK EXECUTED ({conf['risk_tier']})")
188
- print(f"======================================================================")
188
+ print("======================================================================")
189
189
  print(f" Branch Name : {res['branch']}")
190
190
  print(f" PR Title : {res['pr_title']}")
191
191
  print(f" Confidence : {conf['confidence_percentage']}%")
@@ -205,9 +205,9 @@ def main():
205
205
  if args.json:
206
206
  print(json.dumps({"confidence": conf, "pr": pr}, indent=2))
207
207
  else:
208
- print(f"\n======================================================================")
208
+ print("\n======================================================================")
209
209
  print(f" SELF-HEALING CONFIDENCE SCORE: {conf['confidence_percentage']}% ({conf['risk_tier']})")
210
- print(f"======================================================================")
210
+ print("======================================================================")
211
211
  print(f" Branch Name : {pr['branch_name']}")
212
212
  print(f" PR Title : {pr['pr_title']}")
213
213
  print(f" Qualified : {'✅ YES (Auto-PR Recommended)' if conf['qualified_for_pr'] else '⚠️ NO (Manual QA Review Needed)'}\n")
@@ -75,7 +75,7 @@ class HeuristicLocatorMatcher:
75
75
  ("left", "top", "width", "height"),
76
76
  ):
77
77
  if all(k in el for k in keys):
78
- return tuple(float(el[k]) for k in keys)
78
+ return (float(el[keys[0]]), float(el[keys[1]]), float(el[keys[2]]), float(el[keys[3]]))
79
79
  bbox = el.get("bbox")
80
80
  if isinstance(bbox, dict) and all(k in bbox for k in ("x", "y", "width", "height")):
81
81
  return (float(bbox["x"]), float(bbox["y"]),
@@ -270,7 +270,7 @@ class HeuristicLocatorMatcher:
270
270
  ]
271
271
  candidates = [el for el in candidates if cls._get_bbox(el) is not None]
272
272
  if candidates:
273
- target = min(candidates, key=lambda el: cls._center_distance(cls._get_bbox(el), anchor_bbox))
273
+ target = min(candidates, key=lambda el: cls._center_distance(cls._get_bbox(el) or (0.0, 0.0, 0.0, 0.0), anchor_bbox))
274
274
  if target.get("id"):
275
275
  healed = f"#{target['id']}"
276
276
  elif target.get("data-testid") or target.get("test-id"):