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
@@ -7,11 +7,9 @@ Automatically disables dynamic CSS animations, freezes SVG spinners,
7
7
  and masks fluctuating UI elements (timestamps, clocks, avatars, ad banners).
8
8
  """
9
9
 
10
- import os
11
- import re
12
10
  import json
13
11
  import argparse
14
- from typing import List, Dict, Any, Tuple
12
+ from typing import List, Optional
15
13
 
16
14
 
17
15
  class SmartVisualMasker:
@@ -56,13 +54,13 @@ class SmartVisualMasker:
56
54
  return cls.ANTI_FLAKE_CSS.strip()
57
55
 
58
56
  @classmethod
59
- def generate_playwright_ts_options(cls, additional_selectors: List[str] = None) -> str:
57
+ def generate_playwright_ts_options(cls, additional_selectors: Optional[List[str]] = None) -> str:
60
58
  """
61
59
  Generates Playwright TypeScript assertion options with masking and disabled animations.
62
60
  """
63
61
  selectors = cls.DEFAULT_MASK_SELECTORS + (additional_selectors or [])
64
62
  locators_ts = ",\n ".join([f"page.locator('{sel}')" for sel in selectors])
65
-
63
+
66
64
  return f"""await expect(page).toHaveScreenshot('visual-baseline.png', {{
67
65
  animations: 'disabled',
68
66
  caret: 'hide',
@@ -74,7 +72,7 @@ class SmartVisualMasker:
74
72
  }});"""
75
73
 
76
74
  @classmethod
77
- def generate_browser_mask_script(cls, selectors: List[str] = None) -> str:
75
+ def generate_browser_mask_script(cls, selectors: Optional[List[str]] = None) -> str:
78
76
  """
79
77
  Generates JavaScript snippet to run inside the live browser
80
78
  to immediately freeze DOM animations and paint solid black masks
@@ -206,7 +206,6 @@ def compute_drift(spec_criteria: List[Dict[str, Any]], code_files: List[Tuple[st
206
206
  all_code_actions.extend([(file_path, a) for a in actions])
207
207
  combined_code += "\n" + content
208
208
 
209
- combined_code_lower = combined_code.lower()
210
209
  code_tokens = tokenize_text(combined_code)
211
210
 
212
211
  covered_criteria = []
@@ -289,7 +288,7 @@ def generate_markdown_report(result: Dict[str, Any], spec_file: str, code_path:
289
288
  md.append("> Generated autonomously by Synapse-Hub `tools/spec_drift_sentinel.py` ")
290
289
  md.append(f"> **Specification:** `{spec_file}` | **Code Target:** `{code_path}` \n")
291
290
  md.append("---")
292
-
291
+
293
292
  status_icon = "✅" if result["passed"] else "❌"
294
293
  md.append(f"## 📊 Executive Summary: {status_icon} [{result['status']}]")
295
294
  md.append(f"- **Alignment Score:** `{result['alignment_score']}%` (Pass threshold: >= 80% & zero Deficit Drift)")
@@ -216,8 +216,8 @@ class SweQaAci:
216
216
  f.write(new_content)
217
217
 
218
218
  # Diff summary formatting
219
- target_lines = [f"- {l}" for l in norm_target.splitlines()]
220
- replacement_lines = [f"+ {l}" for l in norm_replacement.splitlines()]
219
+ target_lines = [f"- {line}" for line in norm_target.splitlines()]
220
+ replacement_lines = [f"+ {line}" for line in norm_replacement.splitlines()]
221
221
  diff_summary = "\n".join(target_lines + replacement_lines)
222
222
 
223
223
  return {
@@ -19,7 +19,7 @@ import os
19
19
  import sys
20
20
  import json
21
21
  import argparse
22
- from typing import List, Dict, Any, Tuple
22
+ from typing import List, Dict, Any, Optional
23
23
 
24
24
  # Enforce UTF-8 stdout encoding for Windows console compatibility
25
25
  if hasattr(sys.stdout, 'reconfigure'):
@@ -96,7 +96,7 @@ BENCHMARK_CASES = [
96
96
  class SynapseQABench:
97
97
  """Evaluates agent tool-calling capabilities against standardized QA benchmark cases."""
98
98
 
99
- def __init__(self, cases: List[Dict[str, Any]] = None):
99
+ def __init__(self, cases: Optional[List[Dict[str, Any]]] = None):
100
100
  self.cases = cases or BENCHMARK_CASES
101
101
 
102
102
  def run_benchmark(self, tool_resolver_fn=None) -> Dict[str, Any]:
@@ -112,7 +112,7 @@ class SynapseQABench:
112
112
  details = []
113
113
 
114
114
  for case in self.cases:
115
- expected_tool = case["expected_tool"]
115
+ expected_tool = str(case["expected_tool"])
116
116
  # Baseline keyword resolver simulation
117
117
  selected_tool = expected_tool if tool_resolver_fn is None else tool_resolver_fn(case["intent"])
118
118
 
@@ -47,9 +47,10 @@ HEADER_HASH_TEMPLATE = """# ====================================================
47
47
 
48
48
 
49
49
  def generate_claude_md():
50
+ """Generates the unified CLAUDE.md file compiling agent rules and standards."""
50
51
  return f"""# CLAUDE.md — Synapse-Hub Guidelines for Claude Code
51
52
 
52
- > **Author & Creator:** Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
53
+ > **Author & Creator:** Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
53
54
  > **License:** MIT License (Mandatory Attribution Preservation)
54
55
 
55
56
  {HEADER_TEMPLATE}
@@ -91,6 +92,7 @@ In accordance with the MIT License, the original copyright notice crediting **Th
91
92
  5. **Pragmatic Communication Protocol:**
92
93
  - Conversational responses, explanation, and telemetry MUST be in Vietnamese.
93
94
  - All codebase assets, test scripts, Page Classes, docstrings, and commits MUST remain strictly in English.
95
+ - English-First Document Ingestion: When reading bilingual docs (e.g. ARCHITECTURE.en.md vs ARCHITECTURE.md), always read the English version (*.en.md) to save 50–65% BPE tokens.
94
96
 
95
97
  6. **Lean Senior Dev Protocol (The 7-Rung Ladder & YAGNI):**
96
98
  - The best code is the code never written. Solve the whole problem with the least new code.
@@ -100,6 +102,7 @@ In accordance with the MIT License, the original copyright notice crediting **Th
100
102
 
101
103
 
102
104
  def generate_cursorrules():
105
+ """Generates the .cursorrules file compiling agent rules for Cursor AI."""
103
106
  return f"""# .cursorrules — Synapse-Hub Guidelines for Cursor AI
104
107
  # Author & Creator: Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
105
108
  # License: MIT License (Mandatory Attribution Preservation)
@@ -127,6 +130,7 @@ def generate_cursorrules():
127
130
 
128
131
 
129
132
  def generate_windsurfrules():
133
+ """Generates the .windsurfrules configuration for Windsurf AI."""
130
134
  return f"""# .windsurfrules — Synapse-Hub Guidelines for Windsurf AI
131
135
  # Author & Creator: Thế Vị (@TeeVee998) <https://github.com/TeeVee998/Synapse-Hub>
132
136
  # License: MIT License (Mandatory Attribution Preservation)
@@ -153,6 +157,7 @@ def generate_windsurfrules():
153
157
 
154
158
 
155
159
  def generate_cursor_mdc():
160
+ """Generates the .cursor/rules/synapse-qa.mdc file for Cursor IDE."""
156
161
  return f"""---
157
162
  description: Synapse-Hub Enterprise QA Automation & Testing Rules
158
163
  globs: **/*
@@ -177,12 +182,13 @@ Attribution notices must be preserved.
177
182
 
178
183
 
179
184
  def generate_copilot_instructions():
185
+ """Generates the .github/copilot-instructions.md file for GitHub Copilot."""
180
186
  return f"""# GitHub Copilot Custom Instructions for Synapse-Hub
181
187
 
182
188
  {HEADER_TEMPLATE}
183
189
 
184
- > **Project:** Synapse-Hub Enterprise QA Automation
185
- > **Author:** Thế Vị (@TeeVee998) — MIT License
190
+ > **Project:** Synapse-Hub Enterprise QA Automation
191
+ > **Author:** Thế Vị (@TeeVee998) — MIT License
186
192
 
187
193
  ## Coding Invariants
188
194
  - Enforce Clean Page Object Model (POM) with Flat Architecture.
@@ -210,6 +216,14 @@ TARGET_GENERATORS = [
210
216
 
211
217
 
212
218
  def sync_all(check_only=False):
219
+ """Synchronizes AGENTS.md rules across all IDE and assistant bridge files.
220
+
221
+ Args:
222
+ check_only: If True, only checks for rule drift without modifying files.
223
+
224
+ Returns:
225
+ 0 if clean or synchronized, 1 if drift was detected in check_only mode.
226
+ """
213
227
  drift_detected = False
214
228
  for rel_path, gen_fn in TARGET_GENERATORS:
215
229
  full_path = os.path.join(REPO_ROOT, rel_path)
@@ -249,6 +263,7 @@ def sync_all(check_only=False):
249
263
 
250
264
 
251
265
  def main():
266
+ """CLI entrypoint for synchronizing AI agent rules."""
252
267
  parser = argparse.ArgumentParser(description="Synchronize AGENTS.md to all AI IDE bridges")
253
268
  parser.add_argument("--check", action="store_true", help="Check for drift without modifying files")
254
269
  args = parser.parse_args()
@@ -36,7 +36,7 @@ import csv
36
36
  import json
37
37
  import math
38
38
  import argparse
39
- from typing import Dict, List, Any, Optional, Tuple
39
+ from typing import Dict, List, Any
40
40
 
41
41
  # Enforce UTF-8 stdout encoding for Windows/macOS/Linux console compatibility
42
42
  if hasattr(sys.stdout, "reconfigure"):
@@ -286,7 +286,8 @@ def main():
286
286
  args = parser.parse_args()
287
287
 
288
288
  if args.sample or not args.jtl or not args.server_metrics:
289
- import tempfile, time
289
+ import tempfile
290
+ import time
290
291
  temp_dir = tempfile.gettempdir()
291
292
  temp_jtl = os.path.join(temp_dir, "sample_corr_jmeter.jtl")
292
293
  temp_srv = os.path.join(temp_dir, "sample_corr_server.csv")
@@ -175,7 +175,8 @@ def get_git_modified_files(repo_root=None):
175
175
  stdout=subprocess.PIPE,
176
176
  stderr=subprocess.PIPE,
177
177
  text=True,
178
- check=True
178
+ check=True,
179
+ timeout=30.0
179
180
  )
180
181
  files = [line.strip() for line in res.stdout.splitlines() if line.strip()]
181
182
  return files
@@ -27,6 +27,7 @@ import re
27
27
  import json
28
28
  import sqlite3
29
29
  import time
30
+ import uuid
30
31
  import difflib
31
32
  import argparse
32
33
  from datetime import datetime, timezone, timedelta
@@ -48,6 +49,15 @@ SECRET_PATTERNS = [
48
49
  ]
49
50
 
50
51
 
52
+ class ManagedConnection(sqlite3.Connection):
53
+ """SQLite connection that automatically commits/rollbacks and closes on context exit."""
54
+ def __exit__(self, exc_type, exc_val, exc_tb):
55
+ try:
56
+ super().__exit__(exc_type, exc_val, exc_tb)
57
+ finally:
58
+ self.close()
59
+
60
+
51
61
  class TestMemoryOS:
52
62
  """3-Tier Hierarchical QA Memory OS."""
53
63
  __test__ = False
@@ -62,8 +72,8 @@ class TestMemoryOS:
62
72
  os.makedirs(os.path.dirname(os.path.abspath(self.db_path)), exist_ok=True)
63
73
  self._init_db()
64
74
 
65
- def _get_connection(self) -> sqlite3.Connection:
66
- conn = sqlite3.connect(self.db_path)
75
+ def _get_connection(self) -> ManagedConnection:
76
+ conn = sqlite3.connect(self.db_path, factory=ManagedConnection)
67
77
  conn.row_factory = sqlite3.Row
68
78
  return conn
69
79
 
@@ -165,7 +175,7 @@ class TestMemoryOS:
165
175
  INSERT INTO recall_memory (session_id, event_type, actor, payload_json, timestamp)
166
176
  VALUES (?, ?, ?, ?, ?)
167
177
  """, (session_id, event_type, actor, payload_str, now))
168
- return cursor.lastrowid
178
+ return cursor.lastrowid or 0
169
179
 
170
180
  def recall_log(self, session_id: str, limit: int = 50) -> List[Dict[str, Any]]:
171
181
  """Retrieves recent sequential steps for a session."""
@@ -199,7 +209,7 @@ class TestMemoryOS:
199
209
  """Inserts long-term searchable knowledge into archival memory."""
200
210
  payload_str = json.dumps(payload) if not isinstance(payload, str) else payload
201
211
  now = datetime.now(timezone.utc).isoformat()
202
- mem_id = f"mem_arch_{int(time.time() * 1000) % 10000000}"
212
+ mem_id = f"mem_arch_{int(time.time() * 1000) % 10000000}_{uuid.uuid4().hex[:6]}"
203
213
 
204
214
  # Flatten text content for semantic/fuzzy search
205
215
  text_content = f"{key} {aut_version} {tags} {payload_str}".lower()
@@ -336,7 +346,7 @@ def main():
336
346
  subparsers = parser.add_subparsers(dest="subcommand", help="Subcommand to execute")
337
347
 
338
348
  # init
339
- init_parser = subparsers.add_parser("init", help="Initialize memory OS database")
349
+ subparsers.add_parser("init", help="Initialize memory OS database")
340
350
 
341
351
  # update-core
342
352
  uc_parser = subparsers.add_parser("update-core", help="Update core memory block")
@@ -20,7 +20,7 @@ import sys
20
20
  import re
21
21
  import json
22
22
  import argparse
23
- from typing import List, Dict, Any, Tuple, Optional
23
+ from typing import List, Dict, Any, Tuple
24
24
 
25
25
  # Enforce UTF-8 stdout encoding for Windows console compatibility
26
26
  if hasattr(sys.stdout, 'reconfigure'):
@@ -33,10 +33,10 @@ if hasattr(sys.stdout, 'reconfigure'):
33
33
  SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
34
34
  sys.path.insert(0, SCRIPT_DIR)
35
35
 
36
- import micro_mutator
36
+ import micro_mutator # noqa: E402
37
37
 
38
38
  try:
39
- import textgrad
39
+ import textgrad # noqa: F401, E402
40
40
  TEXTGRAD_AVAILABLE = True
41
41
  except ImportError:
42
42
  TEXTGRAD_AVAILABLE = False
@@ -78,7 +78,7 @@ class TMSBridge:
78
78
  "issue_type": "Story",
79
79
  "status": "Ready for QA",
80
80
  "assignee": self.user_email,
81
- "description": f"As an Enterprise Accountant, I want to reconcile invoices matching ERP records to ensure financial accuracy.",
81
+ "description": "As an Enterprise Accountant, I want to reconcile invoices matching ERP records to ensure financial accuracy.",
82
82
  "acceptance_criteria": [
83
83
  "Given the accountant is logged in with FINANCE_ROLE",
84
84
  "When navigating to '/billing/invoices' and filtering by date '2026-10-01'",
@@ -161,8 +161,8 @@ class TMSBridge:
161
161
  bug_key = f"{proj_clean}-BUG-{int(datetime.now().timestamp()) % 10000}"
162
162
 
163
163
  repro = reproduction_steps or [
164
- f"1. Launch headless browser session via Playwright Live.",
165
- f"2. Navigate to target application flow under test.",
164
+ "1. Launch headless browser session via Playwright Live.",
165
+ "2. Navigate to target application flow under test.",
166
166
  f"3. Execute test action sequence for '{test_case_id}'.",
167
167
  f"4. Observe failure: {error_message}"
168
168
  ]
@@ -299,9 +299,9 @@ def main():
299
299
  if args.json:
300
300
  print(json.dumps(story, indent=2))
301
301
  else:
302
- print(f"\n======================================================================")
302
+ print("\n======================================================================")
303
303
  print(f" ENTERPRISE ALM STORY PULL: [{story.get('issue_key')}]")
304
- print(f"======================================================================")
304
+ print("======================================================================")
305
305
  print(f" Summary : {story.get('summary')}")
306
306
  print(f" Status : {story.get('status')} | Assignee: {story.get('assignee')}")
307
307
  print(f" Source : {story.get('source')}\n")
@@ -21,9 +21,8 @@ import os
21
21
  import sys
22
22
  import re
23
23
  import json
24
- import time
25
24
  import argparse
26
- from typing import Dict, Any, List, Optional, Tuple
25
+ from typing import Dict, Any, List, Optional, cast
27
26
 
28
27
  # Enforce UTF-8 stdout encoding
29
28
  if hasattr(sys.stdout, 'reconfigure'):
@@ -43,7 +42,7 @@ class TotExploratoryTester:
43
42
  def expand_story(self, story_content: str, max_depth: int = 3, beam_width: int = 3) -> Dict[str, Any]:
44
43
  """Decomposes story into a 3-branch Tree of Thoughts with risk scores."""
45
44
  # Extract title and criteria
46
- lines = [l.strip() for l in story_content.splitlines() if l.strip()]
45
+ lines = [line.strip() for line in story_content.splitlines() if line.strip()]
47
46
  title = lines[0] if lines else "Untitled User Story"
48
47
  if title.startswith("#"):
49
48
  title = title.lstrip("#").strip()
@@ -93,7 +92,7 @@ class TotExploratoryTester:
93
92
  ]
94
93
 
95
94
  # 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)
95
+ sorted_branches = sorted(branches, key=lambda b: cast(float, b["risk_score"]), reverse=True)
97
96
  selected_branches = sorted_branches[:beam_width]
98
97
 
99
98
  return {
@@ -169,7 +168,7 @@ class TotExploratoryTester:
169
168
  "evidence": evidence
170
169
  })
171
170
 
172
- scored_hypotheses.sort(key=lambda h: h["confidence_score"], reverse=True)
171
+ scored_hypotheses.sort(key=lambda h: cast(float, h["confidence_score"]), reverse=True)
173
172
  winner = scored_hypotheses[0]
174
173
 
175
174
  recommendations = {
@@ -184,7 +183,7 @@ class TotExploratoryTester:
184
183
  "status": "PASS",
185
184
  "primary_root_cause": winner["hypothesis"],
186
185
  "confidence": winner["confidence_score"],
187
- "recommended_fix": recommendations.get(winner["hypothesis"], "Review stacktrace with team"),
186
+ "recommended_fix": recommendations.get(str(winner["hypothesis"]), "Review stacktrace with team"),
188
187
  "ranked_hypotheses": scored_hypotheses
189
188
  }
190
189
 
@@ -23,7 +23,7 @@ import re
23
23
  import sys
24
24
  import json
25
25
  import argparse
26
- from typing import Dict, Any, List, Optional
26
+ from typing import Dict, Any, List
27
27
  from urllib.parse import urlparse
28
28
 
29
29
  # Enforce UTF-8 stdout encoding for Windows console compatibility
@@ -153,14 +153,14 @@ class TrafficConverter:
153
153
  lines = [
154
154
  "import { test, expect } from '@playwright/test';",
155
155
  "",
156
- f"/**",
157
- f" * Automated Replay of Production Traffic Session",
158
- f" * Generated by traffic_session_converter.py (agent-skills Shift-Right Engine)",
156
+ "/**",
157
+ " * Automated Replay of Production Traffic Session",
158
+ " * Generated by traffic_session_converter.py (agent-skills Shift-Right Engine)",
159
159
  f" * Total Actions: {len(requests)} verified network interactions",
160
- f" */",
160
+ " */",
161
161
  f"test.describe('{test_name}', () => {{",
162
- f" test('replays production user journey with PII masking', async ({{ page, request }}) => {{",
163
- f" // 1. Base navigation",
162
+ " test('replays production user journey with PII masking', async ({ page, request }) => {",
163
+ " // 1. Base navigation",
164
164
  f" await page.goto('{base_url}');",
165
165
  ""
166
166
  ]
@@ -185,9 +185,9 @@ class TrafficConverter:
185
185
  formatted_body = json.dumps(body_str)
186
186
 
187
187
  lines.append(f" const resp{step_num} = await request.{method.lower()}('{url}', {{")
188
- lines.append(f" headers: {{ 'Content-Type': 'application/json' }},")
188
+ lines.append(" headers: { 'Content-Type': 'application/json' },")
189
189
  lines.append(f" data: {formatted_body}")
190
- lines.append(f" }});")
190
+ lines.append(" });")
191
191
  lines.append(f" expect(resp{step_num}.status()).toBe({status});")
192
192
  else:
193
193
  lines.append(f" const resp{step_num} = await request.get('{url}');")
@@ -9,8 +9,9 @@ whether the target is Playwright, Selenium, Cypress, Katalon, or Appium.
9
9
 
10
10
  from enum import Enum
11
11
  from typing import Dict, List, Optional, Any
12
- from dataclasses import dataclass, field, asdict
12
+ from dataclasses import dataclass, field
13
13
  import json
14
+ import re
14
15
 
15
16
 
16
17
  class ActionType(str, Enum):
@@ -345,8 +346,6 @@ class ProjectIR:
345
346
  return cls.from_dict(data)
346
347
 
347
348
 
348
- import re
349
-
350
349
  SHARED_VAR_PATTERNS = [
351
350
  (r"\b(?:created_id|order_id|user_id|item_id|customer_id|invoice_id)\b", DataDependencyType.ID),
352
351
  (r"\b(?:auth_token|bearer_token|access_token|jwt_token|token)\b", DataDependencyType.AUTH_TOKEN),
@@ -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.7.0")
68
+ return data.get("version", "3.0.1")
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.7.0")
77
+ return data.get("version", "3.0.1")
78
78
  except (OSError, json.JSONDecodeError):
79
79
  data = {}
80
80
 
81
- return "2.7.0"
81
+ return "3.0.1"
82
82
 
83
83
 
84
84
  def fetch_remote_version():
@@ -259,7 +259,6 @@ def perform_update():
259
259
  git_dir = os.path.join(HUB_ROOT, ".git")
260
260
  is_git_repo = os.path.exists(git_dir)
261
261
 
262
- updated = False
263
262
 
264
263
  if is_git_repo:
265
264
  print(f"\n{BOLD}[1/3] Pulling latest updates via Git...{RESET}")
@@ -281,7 +280,6 @@ def perform_update():
281
280
  print(f" {RED}⚠️ Provenance Warning: {msg}{RESET}")
282
281
  else:
283
282
  print(f" {GREEN}[OK] {msg}{RESET}")
284
- updated = True
285
283
  # If this is the first pull, show the welcome landing page
286
284
  try:
287
285
  import welcome_page
@@ -306,9 +304,8 @@ def perform_update():
306
304
  )
307
305
  if res.returncode == 0:
308
306
  print(f" {GREEN}[OK] Global NPM package updated to latest!{RESET}")
309
- updated = True
310
307
  except (subprocess.SubprocessError, OSError):
311
- updated = False
308
+ _ = False
312
309
 
313
310
  # 2. Check Global Plugin
314
311
  if os.path.exists(GLOBAL_PLUGIN_DIR) and GLOBAL_PLUGIN_DIR.lower() != HUB_ROOT.lower():
@@ -319,7 +316,7 @@ def perform_update():
319
316
  subprocess.run(["git", "pull", "--ff-only"], cwd=GLOBAL_PLUGIN_DIR, stdout=subprocess.DEVNULL, timeout=30.0)
320
317
  print(f" {GREEN}[OK] Updated Global Plugin repository: {GLOBAL_PLUGIN_DIR}{RESET}")
321
318
  except (subprocess.SubprocessError, OSError):
322
- plugin_synced = False
319
+ _ = False
323
320
  else:
324
321
  # Refresh metadata files in global plugin
325
322
  try:
@@ -331,7 +328,7 @@ def perform_update():
331
328
  shutil.copy2(src_f, dst_f)
332
329
  print(f" {GREEN}[OK] Refreshed metadata & LICENSE in: {GLOBAL_PLUGIN_DIR}{RESET}")
333
330
  except OSError:
334
- copy_done = False
331
+ _ = False
335
332
 
336
333
  # 3. Re-initialize SQLite Memory Cache
337
334
  print(f"\n{BOLD}[3/3] Synchronizing Central Agent Memory Cache...{RESET}")
@@ -136,6 +136,7 @@ def resolve_target_repo(cli_target=None, cli_repo=None):
136
136
 
137
137
 
138
138
  def gate_0_author_integrity(repo_root, mode="AUTO"):
139
+ """Gate 0: Verifies author attribution and MIT license provenance across source manifests."""
139
140
  print("\n[GATE 0] Verifying Author Attribution & MIT License Integrity...")
140
141
  try:
141
142
  from author_integrity import (
@@ -194,6 +195,7 @@ MOJIBAKE_INDICATORS = (
194
195
 
195
196
 
196
197
  def gate_1_bom_check(repo_root, mode="AUTO"):
198
+ """Gate 1: Verifies 100% UTF-8 encoding with zero Byte Order Mark (BOM) bytes."""
197
199
  print("\n[GATE 1] Scanning UTF-8 No BOM and Encoding Integrity across repository...")
198
200
  ignored_dirs = {".git", ".gradle", "node_modules", "allure-results", "graphify-out", "__pycache__", "build", "dist", "claude-ecosystem-repos", "external_repos", "teamwork"}
199
201
  ignored_files = {"verify_all.py", "test_tools.py"}
@@ -241,6 +243,7 @@ def gate_1_bom_check(repo_root, mode="AUTO"):
241
243
 
242
244
 
243
245
  def gate_2_path_portability(repo_root, mode="AUTO"):
246
+ """Gate 2: Scans for and rejects machine-specific absolute filepaths in code."""
244
247
  print("\n[GATE 2] Checking Path Portability (Zero absolute machine paths in repo sources)...")
245
248
  ignored_dirs = {
246
249
  ".git", ".gradle", "node_modules", "allure-results", "Screenshots",
@@ -286,13 +289,14 @@ def gate_2_path_portability(repo_root, mode="AUTO"):
286
289
 
287
290
 
288
291
  def gate_3_clean_pom(repo_root, mode="AUTO"):
292
+ """Gate 3: Enforces Clean Page Object Model by rejecting raw selectors in test scripts."""
289
293
  print("\n[GATE 3] Checking Clean POM & Flat Architecture in Test Scripts...")
290
294
  effective_mode = detect_repo_mode(repo_root) if mode == "AUTO" else mode
291
295
  if effective_mode == "HUB":
292
296
  print("[N/A] Hub Mode: Not Applicable (Clean POM rules apply to target test suites in Spokes).")
293
297
  return True
294
298
 
295
- test_dirs = ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
299
+ test_dirs = ["tests"] if effective_mode == "HUB" else ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
296
300
  test_files = []
297
301
  for td in test_dirs:
298
302
  d = os.path.join(repo_root, td)
@@ -346,6 +350,7 @@ def gate_3_clean_pom(repo_root, mode="AUTO"):
346
350
 
347
351
 
348
352
  def gate_4_ai_docs_lint(repo_root, mode="AUTO"):
353
+ """Gate 4: Reconciles PROJECT_AI_GUIDE.md against physical AST to prevent hallucinated classes."""
349
354
  print("\n[GATE 4] Running AI Documentation & Artifact Linter...")
350
355
  linter_script = os.path.join(SCRIPT_DIR, "lint_ai_docs.py")
351
356
  if not os.path.exists(linter_script):
@@ -368,6 +373,7 @@ def gate_4_ai_docs_lint(repo_root, mode="AUTO"):
368
373
 
369
374
 
370
375
  def gate_5_cleanup_blacklist(repo_root, mode="AUTO"):
376
+ """Gate 5: Scans for prohibited temporary files, heap dumps, and local cache artifacts."""
371
377
  print("\n[GATE 5] Checking Temporary File Blacklist...")
372
378
  blacklist_patterns = [
373
379
  os.path.join(repo_root, "Reports", "batch*.properties"),
@@ -392,6 +398,7 @@ def gate_5_cleanup_blacklist(repo_root, mode="AUTO"):
392
398
 
393
399
 
394
400
  def gate_6_credential_security(repo_root, mode="AUTO"):
401
+ """Gate 6: Scans for plaintext passwords, API tokens, and private keys across codebase."""
395
402
  print("\n[GATE 6] Checking Credential Security across repository...")
396
403
  violations = []
397
404
 
@@ -658,6 +665,7 @@ def check_python_ast_integrity(repo_root):
658
665
 
659
666
 
660
667
  def gate_7_infer_linter(repo_root, mode="AUTO"):
668
+ """Gate 7: Runs Meta Infer-style static defect sentinel catching fake passes, 0 assert, and sleeps."""
661
669
  print("\n[GATE 7] Running Meta Infer-Style Static Defect & Fake-Pass Sentinel...")
662
670
  effective_mode = detect_repo_mode(repo_root) if mode == "AUTO" else mode
663
671
 
@@ -700,7 +708,7 @@ def gate_7_infer_linter(repo_root, mode="AUTO"):
700
708
  except (ImportError, OSError) as err:
701
709
  print(f"[WARN] Failed to run check_version_sync: {err}")
702
710
 
703
- test_dirs = ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
711
+ test_dirs = ["tests"] if effective_mode == "HUB" else ["Scripts", "tests", "test", "e2e", "specs", "cypress"]
704
712
  has_tests = False
705
713
  target_scan_dir = None
706
714
  for td in test_dirs:
@@ -737,6 +745,7 @@ def gate_7_infer_linter(repo_root, mode="AUTO"):
737
745
 
738
746
 
739
747
  def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
748
+ """Gate 8: Runs syntactic mutation testing and verifies assertion sensitivity score >= 80%."""
740
749
  eval_mode_desc = f"DYNAMIC EXECUTION MODE ({test_cmd})" if test_cmd else "HEURISTIC STATIC MODE"
741
750
  print(f"\n[GATE 8] Running Assertion Sensitivity & Syntactic Mutation Sentinel [{eval_mode_desc}] (Min Score >= 80%)...")
742
751
  scripts_dir = os.path.join(repo_root, "Scripts")
@@ -797,6 +806,7 @@ def gate_8_micro_mutator(repo_root, mode="AUTO", test_cmd=""):
797
806
 
798
807
 
799
808
  def gate_9_skill_collision(repo_root, mode="AUTO"):
809
+ """Gate 9: Scans skill descriptions to ensure semantic trigger overlap remains < 0.75."""
800
810
  print("\n[GATE 9] Running Tier-2 Skill Routing & Collision Guard (Addy Osmani Pattern)...")
801
811
  guard_script = os.path.join(SCRIPT_DIR, "skill_collision_guard.py")
802
812
  if not os.path.exists(guard_script):
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
- from __future__ import annotations
4
3
  """
5
4
  Enterprise Canvas Visual Diff & Regression Checker
6
5
  ===================================================
@@ -17,11 +16,11 @@ Usage:
17
16
  python3 tools/visual_diff_canvas.py compare baseline.png current.png --diff diff.png --threshold 0.02
18
17
  python3 tools/visual_diff_canvas.py check-blank current.png
19
18
  """
19
+ from __future__ import annotations
20
20
 
21
21
  import os
22
22
  import sys
23
23
  import argparse
24
- from pathlib import Path
25
24
 
26
25
  try:
27
26
  from PIL import Image, ImageChops, ImageStat
@@ -56,11 +55,12 @@ def compare_canvas_images(
56
55
  """
57
56
  with Image.open(baseline_path) as img_base, Image.open(current_path) as img_curr:
58
57
  # Normalize dimensions and color mode
58
+ curr_image: Image.Image = img_curr
59
59
  if img_base.size != img_curr.size:
60
- img_curr = img_curr.resize(img_base.size, Image.Resampling.LANCZOS)
60
+ curr_image = img_curr.resize(img_base.size, Image.Resampling.LANCZOS)
61
61
 
62
62
  base_rgb = img_base.convert("RGB")
63
- curr_rgb = img_curr.convert("RGB")
63
+ curr_rgb = curr_image.convert("RGB")
64
64
 
65
65
  # C-level accelerated pixel difference via Pillow vectorization
66
66
  diff = ImageChops.difference(base_rgb, curr_rgb)