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.
- package/.agents/agents/live-explorer.md +2 -0
- package/.agents/agents/pom-crafter.md +2 -0
- package/.agents/agents/test-runner.md +3 -0
- package/.agents/rules/communication-style.md +4 -0
- package/.agents/rules/context-engineering.md +1 -0
- package/.agents/rules/git-push-protocol.md +17 -0
- package/.agents/skills/agentless-qa-repair/examples/three_phase_repair_workflow.md +40 -0
- package/.agents/skills/antigravity-test-sentinel/examples/sprint_dod_enforcement.md +47 -0
- package/.agents/skills/auth-session-and-cookie-manager/examples/multi_role_auth_matrix.md +70 -0
- package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_playwright_pom.ts +52 -0
- package/.agents/skills/failure-autopsy-and-healing/examples/closed_loop_healing_walkthrough.md +48 -0
- package/.agents/skills/lean-impact-scoreboard/SKILL.md +1 -1
- package/.agents/skills/lean-impact-scoreboard/examples/sample_scoreboard.md +1 -1
- package/.agents/skills/mipro-prompt-compiler/examples/dspy_assertion_optimizer_case.md +46 -0
- package/.agents/skills/persistent-planning-files/SKILL.md +1 -1
- package/.agents/skills/qa-aci-interface/examples/surgical_code_window_walkthrough.md +40 -0
- package/.agents/skills/repo-quality-audit/SKILL.md +1 -1
- package/.agents/skills/review-spec/SKILL.md +1 -1
- package/.agents/skills/review-spec/examples/pocock_grilling_decision_rubric.md +40 -0
- package/.agents/skills/senior-code-review/SKILL.md +1 -1
- package/.agents/skills/skillspector-security-guard/examples/audit_threat_vectors_report.json +35 -0
- package/.agents/skills/surgical-coding-principles/SKILL.md +1 -1
- package/.agents/skills/technical-debt-harvester/SKILL.md +1 -1
- package/.agents/skills/tot-exploratory-testing/examples/payment_gateway_tot_tree.json +62 -0
- package/.agents/skills/verification-before-completion/SKILL.md +2 -2
- package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +2 -2
- package/.agents/skills/web-agent-gym-benchmarking/examples/enterprise_tasks_suite.json +38 -0
- package/.agents/skills/web-vitals-and-performance-profiler/examples/playwright_cwv_profiler_fixture.ts +61 -0
- package/AGENTS.md +6 -4
- package/CLAUDE.md +2 -1
- package/README.en.md +71 -292
- package/README.md +69 -304
- package/action.yml +1 -1
- package/bin/agent-qa.js +2 -2
- package/docs/ARCHITECTURE.en.md +192 -0
- package/docs/ARCHITECTURE.md +135 -88
- package/docs/CI_CD_INTEGRATION.en.md +96 -0
- package/docs/CI_CD_INTEGRATION.md +24 -22
- package/docs/CLI_REFERENCE.en.md +174 -0
- package/docs/CLI_REFERENCE.md +120 -115
- package/docs/CONTRIBUTING.en.md +49 -0
- package/docs/CONTRIBUTING.md +26 -68
- package/docs/QUALITY_GATES.en.md +115 -0
- package/docs/QUALITY_GATES.md +115 -0
- package/docs/research/agent_skill_training_analysis.md +1 -1
- package/package.json +1 -1
- package/scripts/package_dist.py +2 -1
- package/skills_manifest.json +6 -6
- package/synapse.config.json +1 -1
- package/templates/ai_bridges/CLAUDE.md +2 -1
- package/templates/ai_bridges/copilot-instructions.md +2 -2
- package/tools/__init__.py +18 -1
- package/tools/_openapi_parser.py +1 -1
- package/tools/agent_canary.py +42 -30
- package/tools/agentless_patcher.py +2 -2
- package/tools/analyze_project.py +3 -5
- package/tools/api_drift_sentinel.py +4 -4
- package/tools/api_fuzzer.py +8 -8
- package/tools/auto_heal.py +4 -5
- package/tools/auto_heal_reflexion.py +10 -2
- package/tools/autogen_qa_broker.py +2 -2
- package/tools/browser_helpers.js +2 -2
- package/tools/browsergym_eval.py +2 -3
- package/tools/bug_reporter.py +11 -13
- package/tools/bump_version.py +90 -19
- package/tools/cache_resolver.py +78 -83
- package/tools/check_env.py +9 -1
- package/tools/check_version_sync.py +58 -4
- package/tools/ci_healing_hook.py +4 -6
- package/tools/cli.py +5 -7
- package/tools/config_decoupler.py +2 -3
- package/tools/contract_validator.py +141 -0
- package/tools/core/__init__.py +52 -0
- package/tools/count_tokens.py +22 -20
- package/tools/dashboard_generator.py +629 -1000
- package/tools/db_sandbox.py +87 -62
- package/tools/debt_harvester.py +3 -3
- package/tools/distill_log.py +3 -3
- package/tools/dspy_qa_signatures.py +3 -4
- package/tools/export_postman_collection.py +8 -8
- package/tools/flaky_detector.py +2 -2
- package/tools/fuzz_payloads.py +5 -5
- package/tools/generate_allure_report.py +1 -1
- package/tools/generate_jmx_plan.py +4 -4
- package/tools/gherkin_suite.py +1 -1
- package/tools/git_pre_commit_guard.py +2 -2
- package/tools/goldfish_review.py +2 -2
- package/tools/heal/__init__.py +28 -0
- package/tools/heal_pr_automator.py +6 -6
- package/tools/heuristic_locator_matcher.py +2 -2
- package/tools/infer_linter.py +143 -29
- package/tools/interview_story.py +0 -1
- package/tools/jtl_analyzer.py +11 -11
- package/tools/lint/__init__.py +30 -0
- package/tools/lint_ai_docs.py +42 -0
- package/tools/lookup_tc.py +1 -1
- package/tools/matrix_sharder.py +15 -13
- package/tools/mcp/__init__.py +16 -0
- package/tools/mcp_excel_server.py +2 -2
- package/tools/mcp_server.py +9 -7
- package/tools/md_to_xlsx.py +26 -26
- package/tools/micro_mutator.py +32 -15
- package/tools/network_mock_shield.py +27 -27
- package/tools/newman_analyzer.py +2 -3
- package/tools/omni_emitter.py +5 -8
- package/tools/omni_parser.py +5 -5
- package/tools/omni_transpiler.py +5 -5
- package/tools/output_formatter.py +19 -0
- package/tools/perf/__init__.py +24 -0
- package/tools/pinpoint_failure.py +1 -1
- package/tools/project_state.py +0 -2
- package/tools/qa_event_stream.py +1 -1
- package/tools/repo_map.py +8 -8
- package/tools/report/__init__.py +32 -0
- package/tools/reversible_sandbox.py +3 -4
- package/tools/safety_firewall.py +2 -2
- package/tools/senior_code_review.py +4 -3
- package/tools/setup_project.py +10 -12
- package/tools/skill_collision_guard.py +3 -3
- package/tools/skill_vault.py +94 -6
- package/tools/skillspector.py +10 -8
- package/tools/smart_visual_masker.py +4 -6
- package/tools/spec_drift_sentinel.py +1 -2
- package/tools/swe_qa_aci.py +2 -2
- package/tools/synapse_qa_bench.py +3 -3
- package/tools/sync_agent_rules.py +18 -3
- package/tools/telemetry_correlator.py +3 -2
- package/tools/test_impact.py +2 -1
- package/tools/test_memory_os.py +15 -5
- package/tools/textgrad_mutation_optimizer.py +3 -3
- package/tools/tms_sync_bridge.py +5 -5
- package/tools/tot_exploratory_tester.py +5 -6
- package/tools/traffic_session_converter.py +9 -9
- package/tools/universal_ir.py +2 -3
- package/tools/update_sentinel.py +6 -9
- package/tools/verify_all.py +12 -2
- package/tools/visual_diff_canvas.py +4 -4
- package/tools/visual_som_helper.py +15 -13
- package/tools/watch_mode.py +1 -1
- package/tools/wcag_audit.py +22 -1
- package/tools/welcome_page.py +5 -6
package/tools/db_sandbox.py
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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 {
|
|
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
|
-
#
|
|
191
|
-
cursor.execute("SELECT
|
|
192
|
-
|
|
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 {
|
|
196
|
-
cursor.execute(f"RELEASE SAVEPOINT {
|
|
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
|
-
|
|
200
|
-
|
|
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 {
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
for
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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",
|
package/tools/debt_harvester.py
CHANGED
|
@@ -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
|
"",
|
package/tools/distill_log.py
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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")
|
package/tools/flaky_detector.py
CHANGED
|
@@ -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(
|
|
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(
|
|
337
|
+
print("======================================================================")
|
|
338
338
|
print(f"Quarantine Recommendations: {quarantined_count}/{len(candidates)}")
|
|
339
339
|
sys.exit(0)
|
|
340
340
|
|
package/tools/fuzz_payloads.py
CHANGED
|
@@ -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(
|
|
167
|
+
print("\n======================================================================")
|
|
168
168
|
print(f" EDGE-CASE FUZZING SCENARIOS FOR FIELD: '{args.field}' ({args.type.upper()})")
|
|
169
|
-
print(
|
|
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(
|
|
181
|
-
print(
|
|
182
|
-
print(
|
|
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
|
-
|
|
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
|
|
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.")
|
package/tools/gherkin_suite.py
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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)")
|
package/tools/goldfish_review.py
CHANGED
|
@@ -108,8 +108,8 @@ def evaluate_spec_content(content: str, filename: str = "") -> dict:
|
|
|
108
108
|
findings = []
|
|
109
109
|
vector_scores = {}
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
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(
|
|
186
|
+
print("\n======================================================================")
|
|
187
187
|
print(f" CI/CD SILENT SELF-HEALING HOOK EXECUTED ({conf['risk_tier']})")
|
|
188
|
-
print(
|
|
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(
|
|
208
|
+
print("\n======================================================================")
|
|
209
209
|
print(f" SELF-HEALING CONFIDENCE SCORE: {conf['confidence_percentage']}% ({conf['risk_tier']})")
|
|
210
|
-
print(
|
|
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
|
|
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"):
|