synapse-hub 2.5.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/failure-analyst.md +17 -0
- package/.agents/agents/orchestrator.md +20 -2
- package/.agents/agents/pom-crafter.md +20 -0
- package/.agents/agents/quality-gatekeeper.md +20 -2
- package/.agents/agents/test-runner.md +16 -0
- package/.agents/rules/context-engineering.md +22 -102
- package/.agents/rules/git-push-protocol.md +1 -1
- package/.agents/rules/graphify.md +2 -0
- package/.agents/rules/mandatory-playwright-live.md +9 -9
- package/.agents/rules/mobile_appium_rules.md +27 -116
- package/.agents/rules/performance_testing_rules.md +18 -62
- package/.agents/rules/skill-precedence.md +22 -42
- package/.agents/rules/systematic-debugging.md +10 -29
- package/.agents/skills/actor-qa-swarm/SKILL.md +77 -0
- package/.agents/skills/actor-qa-swarm/examples/broker_sharding_sample.json +12 -0
- package/.agents/skills/agentless-qa-repair/SKILL.md +69 -0
- package/.agents/skills/agentless-qa-repair/examples/healing_report_sample.json +17 -0
- package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +1 -1
- package/.agents/skills/clean-pom-crafter/SKILL.md +2 -2
- package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +4 -4
- package/.agents/skills/event-stream-qa-orchestrator/SKILL.md +79 -0
- package/.agents/skills/event-stream-qa-orchestrator/examples/timeline_sample.jsonl +2 -0
- package/.agents/skills/failure-autopsy-and-healing/SKILL.md +2 -2
- package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +6 -21
- package/.agents/skills/katalon-cli-runner/SKILL.md +1 -1
- package/.agents/skills/mipro-prompt-compiler/SKILL.md +59 -0
- package/.agents/skills/mipro-prompt-compiler/examples/compiled_module_sample.json +22 -0
- package/.agents/skills/qa-aci-interface/SKILL.md +67 -0
- package/.agents/skills/qa-aci-interface/examples/surgical_edit_sample.json +12 -0
- package/.agents/skills/qa-memory-os/SKILL.md +80 -0
- package/.agents/skills/qa-memory-os/examples/archival_memory_sample.json +21 -0
- package/.agents/skills/review-spec/SKILL.md +60 -0
- package/.agents/skills/review-spec/examples/sample_spec_review.md +33 -0
- package/.agents/skills/synthetic-test-data-generator/SKILL.md +2 -2
- package/.agents/skills/token-and-context-optimizer/references/context_engineering_guide.md +49 -0
- package/.agents/skills/tot-exploratory-testing/SKILL.md +59 -0
- package/.agents/skills/tot-exploratory-testing/examples/tot_graph_sample.json +35 -0
- package/.agents/skills/wcag-audit-patterns/SKILL.md +2 -2
- package/.agents/skills/web-agent-gym-benchmarking/SKILL.md +62 -0
- package/.agents/skills/web-agent-gym-benchmarking/examples/benchmark_report_sample.json +22 -0
- package/AGENTS.md +6 -1
- package/CLAUDE.md +1 -1
- package/NOTICE +40 -4
- package/README.en.md +27 -11
- package/README.md +28 -12
- package/docs/ARCHITECTURE.md +39 -11
- package/docs/adopted-ecosystem-tools.md +9 -0
- package/docs/research/EXTERNAL_AGENT_SKILLS_SURVEY.md +1062 -0
- package/docs/research/agent_skill_training_analysis.md +1357 -0
- package/package.json +1 -1
- package/skills_manifest.json +88 -7
- package/synapse.config.json +3 -2
- package/templates/ai_bridges/CLAUDE.md +1 -1
- package/templates/welcome.html.tmpl +34 -25
- package/tools/TOOLS_RUNBOOK.md +130 -1
- package/tools/__init__.py +1 -1
- package/tools/_openapi_parser.py +158 -0
- package/tools/agentless_patcher.py +417 -0
- package/tools/analyze_project.py +9 -6
- package/tools/auto_heal.py +29 -0
- package/tools/auto_heal_reflexion.py +172 -0
- package/tools/autogen_qa_broker.py +318 -0
- package/tools/browser_helpers.js +57 -2
- package/tools/browsergym_eval.py +237 -0
- package/tools/ci_healing_hook.py +1 -1
- package/tools/cli.py +3 -3
- package/tools/dashboard_generator.py +29 -13
- package/tools/db_sandbox.py +4 -0
- package/tools/dspy_qa_signatures.py +357 -0
- package/tools/heal_pr_automator.py +25 -0
- package/tools/interview_story.py +117 -0
- package/tools/lint_ai_docs.py +1 -1
- package/tools/mcp_excel_server.py +1 -1
- package/tools/mcp_server.py +1 -1
- package/tools/mipro_assertion_tuner.py +207 -0
- package/tools/pinpoint_failure.py +12 -0
- package/tools/project_state.py +4 -0
- package/tools/qa_event_stream.py +336 -0
- package/tools/reversible_sandbox.py +161 -0
- package/tools/safety_firewall.py +28 -0
- package/tools/skill_collision_guard.py +231 -0
- package/tools/skill_vault.py +343 -0
- package/tools/swe_qa_aci.py +369 -0
- package/tools/synapse_qa_bench.py +190 -0
- package/tools/sync_agent_rules.py +1 -1
- package/tools/test_memory_os.py +409 -0
- package/tools/textgrad_mutation_optimizer.py +234 -0
- package/tools/tot_exploratory_tester.py +240 -0
- package/tools/update_sentinel.py +3 -3
- package/tools/verify_all.py +35 -4
- package/tools/watch_mode.py +1 -1
- package/tools/welcome_page.py +2 -2
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026 Thế Vị (@TeeVee998). All rights reserved.
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
# Project: Synapse-Hub (https://github.com/TeeVee998/Synapse-Hub)
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
SkillVault Lifelong Page Object Model & Interaction Library (MineDojo / Voyager Pattern)
|
|
8
|
+
---------------------------------------------------------------------------------------
|
|
9
|
+
Adapts Voyager's self-evolving SkillManager into a persistent SQLite store (.synapse/skill_vault.db).
|
|
10
|
+
When pom-crafter authors new test cases, it queries SkillVault to retrieve existing, verified
|
|
11
|
+
Page Class methods and business interaction primitives, preventing redundant locator generation.
|
|
12
|
+
|
|
13
|
+
Reference:
|
|
14
|
+
- Guanzhi Wang et al. (Voyager: An Open-Ended Embodied Agent with Large Language Models).
|
|
15
|
+
- Synapse-Hub Subsystem 1: Section 6.2 of agent_skill_training_analysis.md.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import sqlite3
|
|
21
|
+
import json
|
|
22
|
+
import re
|
|
23
|
+
import difflib
|
|
24
|
+
import argparse
|
|
25
|
+
from dataclasses import dataclass, asdict
|
|
26
|
+
from typing import List, Optional, Dict, Any, Set
|
|
27
|
+
|
|
28
|
+
# Enforce UTF-8 stdout encoding for Windows console compatibility
|
|
29
|
+
if hasattr(sys.stdout, 'reconfigure'):
|
|
30
|
+
try:
|
|
31
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
32
|
+
sys.stderr.reconfigure(encoding='utf-8')
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Domain synonym ontology for enterprise QA & UI interactions (Offline Fuzzy Semantic Matching)
|
|
38
|
+
QA_SYNONYMS: Dict[str, Set[str]] = {
|
|
39
|
+
"auth": {"login", "signin", "sign-in", "authenticate", "sso", "session", "credential", "token", "password", "user", "oauth"},
|
|
40
|
+
"login": {"auth", "signin", "sign-in", "authenticate", "sso", "session", "credential"},
|
|
41
|
+
"table": {"grid", "datatable", "datagrid", "row", "cell", "column", "pagination", "paging", "list", "tbody"},
|
|
42
|
+
"grid": {"table", "datatable", "datagrid", "row", "cell", "column", "pagination"},
|
|
43
|
+
"pagination": {"paging", "page", "pager", "next-page", "previous", "pagination", "table"},
|
|
44
|
+
"approve": {"accept", "confirm", "reconcile", "verify", "validate", "signoff", "approval", "pass"},
|
|
45
|
+
"modal": {"dialog", "popup", "overlay", "backdrop", "drawer", "alert", "window"},
|
|
46
|
+
"dialog": {"modal", "popup", "overlay", "backdrop", "window"},
|
|
47
|
+
"navigate": {"goto", "route", "url", "visit", "open", "link", "redirect"},
|
|
48
|
+
"button": {"btn", "click", "press", "submit", "action", "trigger"},
|
|
49
|
+
"input": {"textbox", "fill", "type", "enter", "field", "value", "text"},
|
|
50
|
+
"dropdown": {"select", "combobox", "option", "menu", "picker", "choice"},
|
|
51
|
+
"invoice": {"billing", "order", "receipt", "payment", "charge", "reconcile"},
|
|
52
|
+
"cart": {"checkout", "basket", "purchase", "item", "order"},
|
|
53
|
+
"upload": {"attachment", "file", "import", "choose-file", "document"},
|
|
54
|
+
"wait": {"detach", "detached", "sleep", "loading", "spinner", "ready", "networkidle", "smartwait"},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _expand_tokens_with_synonyms(tokens: List[str]) -> Set[str]:
|
|
59
|
+
"""Expands query tokens using the QA domain synonym clusters."""
|
|
60
|
+
expanded = set(tokens)
|
|
61
|
+
for t in tokens:
|
|
62
|
+
if t in QA_SYNONYMS:
|
|
63
|
+
expanded.update(QA_SYNONYMS[t])
|
|
64
|
+
for syn_key, cluster in QA_SYNONYMS.items():
|
|
65
|
+
if t in cluster:
|
|
66
|
+
expanded.add(syn_key)
|
|
67
|
+
expanded.update(cluster)
|
|
68
|
+
return expanded
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _calculate_token_match_score(q_token: str, target_tokens: List[str]) -> float:
|
|
72
|
+
"""Computes best match score for a single query token against target corpus tokens."""
|
|
73
|
+
best = 0.0
|
|
74
|
+
for target in target_tokens:
|
|
75
|
+
if q_token == target:
|
|
76
|
+
return 1.0
|
|
77
|
+
if q_token in target or target in q_token:
|
|
78
|
+
best = max(best, 0.8)
|
|
79
|
+
continue
|
|
80
|
+
# Synonym check
|
|
81
|
+
if q_token in QA_SYNONYMS and target in QA_SYNONYMS[q_token]:
|
|
82
|
+
best = max(best, 0.85)
|
|
83
|
+
continue
|
|
84
|
+
# Fuzzy morphological / typo similarity via SequenceMatcher
|
|
85
|
+
ratio = difflib.SequenceMatcher(None, q_token, target).ratio()
|
|
86
|
+
if ratio >= 0.75:
|
|
87
|
+
best = max(best, ratio * 0.8)
|
|
88
|
+
return best
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class PageClassPrimitive:
|
|
93
|
+
skill_id: str # e.g., "SKILL_AUTH_LOGIN_SSO"
|
|
94
|
+
page_class: str # e.g., "LoginPage"
|
|
95
|
+
method_signature: str # e.g., "loginWithSSO(username: str, sso_token: str)"
|
|
96
|
+
method_code: str # Complete executable method body
|
|
97
|
+
locator_tier: int # 1 to 6 (Master Locator Hierarchy)
|
|
98
|
+
semantic_docstring: str # Generated docstring for embedding / retrieval
|
|
99
|
+
verified_on_url: str # Target AUT URL verified against
|
|
100
|
+
mutation_score: float # Verified assertion sensitivity score (>= 80%)
|
|
101
|
+
tags: str = "" # Comma-separated domain tags, e.g. "auth,sso,login"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class SkillVault:
|
|
105
|
+
"""Persistent library of verified, self-contained Page Class methods."""
|
|
106
|
+
|
|
107
|
+
def __init__(self, db_path: Optional[str] = None):
|
|
108
|
+
if db_path is None:
|
|
109
|
+
# Anchor to .synapse in workspace or CWD
|
|
110
|
+
base_dir = os.path.join(os.getcwd(), ".synapse")
|
|
111
|
+
os.makedirs(base_dir, exist_ok=True)
|
|
112
|
+
db_path = os.path.join(base_dir, "skill_vault.db")
|
|
113
|
+
self.db_path = db_path
|
|
114
|
+
self._init_schema()
|
|
115
|
+
|
|
116
|
+
def _get_connection(self) -> sqlite3.Connection:
|
|
117
|
+
conn = sqlite3.connect(self.db_path)
|
|
118
|
+
conn.row_factory = sqlite3.Row
|
|
119
|
+
return conn
|
|
120
|
+
|
|
121
|
+
def _init_schema(self):
|
|
122
|
+
with self._get_connection() as conn:
|
|
123
|
+
conn.execute("""
|
|
124
|
+
CREATE TABLE IF NOT EXISTS page_primitives (
|
|
125
|
+
skill_id TEXT PRIMARY KEY,
|
|
126
|
+
page_class TEXT NOT NULL,
|
|
127
|
+
method_signature TEXT NOT NULL,
|
|
128
|
+
method_code TEXT NOT NULL,
|
|
129
|
+
locator_tier INTEGER DEFAULT 1,
|
|
130
|
+
semantic_docstring TEXT,
|
|
131
|
+
verified_on_url TEXT,
|
|
132
|
+
mutation_score REAL DEFAULT 0.0,
|
|
133
|
+
tags TEXT DEFAULT '',
|
|
134
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
135
|
+
);
|
|
136
|
+
""")
|
|
137
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_page_class ON page_primitives(page_class);")
|
|
138
|
+
conn.execute("CREATE INDEX IF NOT EXISTS idx_method_sig ON page_primitives(method_signature);")
|
|
139
|
+
# Auto-migrate: check if tags column exists
|
|
140
|
+
try:
|
|
141
|
+
cursor = conn.execute("PRAGMA table_info(page_primitives)")
|
|
142
|
+
cols = [row["name"] for row in cursor.fetchall()]
|
|
143
|
+
if "tags" not in cols:
|
|
144
|
+
conn.execute("ALTER TABLE page_primitives ADD COLUMN tags TEXT DEFAULT '';")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
# Column check non-fatal during migration
|
|
147
|
+
sys.stderr.write(f"Notice: table migration skipped: {e}\n")
|
|
148
|
+
|
|
149
|
+
def register_primitive(self, primitive: PageClassPrimitive) -> bool:
|
|
150
|
+
"""Persists a verified Page Class primitive."""
|
|
151
|
+
try:
|
|
152
|
+
with self._get_connection() as conn:
|
|
153
|
+
conn.execute("""
|
|
154
|
+
INSERT OR REPLACE INTO page_primitives (
|
|
155
|
+
skill_id, page_class, method_signature, method_code,
|
|
156
|
+
locator_tier, semantic_docstring, verified_on_url, mutation_score, tags
|
|
157
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
|
158
|
+
""", (
|
|
159
|
+
primitive.skill_id,
|
|
160
|
+
primitive.page_class,
|
|
161
|
+
primitive.method_signature,
|
|
162
|
+
primitive.method_code,
|
|
163
|
+
primitive.locator_tier,
|
|
164
|
+
primitive.semantic_docstring,
|
|
165
|
+
primitive.verified_on_url,
|
|
166
|
+
primitive.mutation_score,
|
|
167
|
+
primitive.tags
|
|
168
|
+
))
|
|
169
|
+
return True
|
|
170
|
+
except sqlite3.Error as e:
|
|
171
|
+
sys.stderr.write(f"SkillVault Error: {e}\n")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
def query_primitives(self, query: str, top_k: int = 5, tag: Optional[str] = None, min_score: float = 0.15) -> List[Dict[str, Any]]:
|
|
175
|
+
"""
|
|
176
|
+
Queries verified primitives using intelligent fuzzy semantic matching:
|
|
177
|
+
combines exact tokens, synonym expansion, n-gram substring match, and SequenceMatcher similarity.
|
|
178
|
+
"""
|
|
179
|
+
raw_tokens = [t.lower() for t in re.findall(r'[a-zA-Z0-9_-]+', query) if len(t) >= 2]
|
|
180
|
+
if not raw_tokens and not tag:
|
|
181
|
+
return self.list_primitives(limit=top_k)
|
|
182
|
+
|
|
183
|
+
expanded_tokens = _expand_tokens_with_synonyms(raw_tokens) if raw_tokens else set()
|
|
184
|
+
|
|
185
|
+
with self._get_connection() as conn:
|
|
186
|
+
if tag:
|
|
187
|
+
rows = conn.execute("SELECT * FROM page_primitives WHERE tags LIKE ?", (f"%{tag}%",)).fetchall()
|
|
188
|
+
else:
|
|
189
|
+
rows = conn.execute("SELECT * FROM page_primitives").fetchall()
|
|
190
|
+
|
|
191
|
+
if not raw_tokens:
|
|
192
|
+
return [dict(r) for r in rows[:top_k]]
|
|
193
|
+
|
|
194
|
+
scored = []
|
|
195
|
+
for r in rows:
|
|
196
|
+
corpus_text = f"{r['page_class']} {r['method_signature']} {r['semantic_docstring'] or ''} {r['tags'] or ''}".lower()
|
|
197
|
+
corpus_tokens = [t.lower() for t in re.findall(r'[a-zA-Z0-9_-]+', corpus_text) if len(t) >= 2]
|
|
198
|
+
|
|
199
|
+
# Calculate individual query token matches
|
|
200
|
+
token_scores = [_calculate_token_match_score(qt, corpus_tokens) for qt in raw_tokens]
|
|
201
|
+
avg_token_score = sum(token_scores) / len(token_scores) if token_scores else 0.0
|
|
202
|
+
|
|
203
|
+
# Synonym boost
|
|
204
|
+
synonym_hits = sum(1 for st in expanded_tokens if st in corpus_text)
|
|
205
|
+
synonym_factor = min(0.3, (synonym_hits / max(1, len(expanded_tokens))) * 0.3)
|
|
206
|
+
|
|
207
|
+
# Mutation score quality boost (up to +0.1 for high mutation score)
|
|
208
|
+
mut_score = r["mutation_score"] or 0.0
|
|
209
|
+
mutation_boost = (mut_score / 100.0) * 0.1
|
|
210
|
+
|
|
211
|
+
total_relevance = round(min(1.0, (avg_token_score * 0.7) + synonym_factor + mutation_boost), 4)
|
|
212
|
+
|
|
213
|
+
# Pass if relevance reaches threshold OR direct substring match exists
|
|
214
|
+
if total_relevance >= min_score or any(qt in corpus_text for qt in raw_tokens):
|
|
215
|
+
item = dict(r)
|
|
216
|
+
item["relevance_score"] = total_relevance
|
|
217
|
+
item["relevance_percentage"] = f"{round(total_relevance * 100, 1)}%"
|
|
218
|
+
scored.append(item)
|
|
219
|
+
|
|
220
|
+
scored.sort(key=lambda x: (x["relevance_score"], x["mutation_score"]), reverse=True)
|
|
221
|
+
return scored[:top_k]
|
|
222
|
+
|
|
223
|
+
def list_primitives(self, limit: int = 50) -> List[Dict[str, Any]]:
|
|
224
|
+
with self._get_connection() as conn:
|
|
225
|
+
rows = conn.execute("SELECT * FROM page_primitives ORDER BY created_at DESC LIMIT ?", (limit,)).fetchall()
|
|
226
|
+
return [dict(r) for r in rows]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def main():
|
|
230
|
+
parser = argparse.ArgumentParser(description="SkillVault Lifelong POM Library")
|
|
231
|
+
parser.add_argument("--query", default=None, help="Query verified primitives by keyword (fuzzy semantic search)")
|
|
232
|
+
parser.add_argument("--tag", default=None, help="Filter primitives by domain tag (e.g. auth, table, invoice)")
|
|
233
|
+
parser.add_argument("--list", action="store_true", help="List all stored primitives")
|
|
234
|
+
parser.add_argument("--seed-samples", action="store_true", help="Seed initial verified POM primitives")
|
|
235
|
+
args = parser.parse_args()
|
|
236
|
+
|
|
237
|
+
vault = SkillVault()
|
|
238
|
+
|
|
239
|
+
if args.seed_samples:
|
|
240
|
+
samples = [
|
|
241
|
+
PageClassPrimitive(
|
|
242
|
+
skill_id="SKILL_AUTH_LOGIN_SSO",
|
|
243
|
+
page_class="LoginPage",
|
|
244
|
+
method_signature="loginWithSSO(username: str, sso_token: str)",
|
|
245
|
+
method_code="""def loginWithSSO(self, username: str, sso_token: str):
|
|
246
|
+
self.page.goto("/auth/sso")
|
|
247
|
+
self.page.getByRole("textbox", name="SSO User").fill(username)
|
|
248
|
+
self.page.getByRole("textbox", name="Token").fill(sso_token)
|
|
249
|
+
self.page.getByRole("button", name="Authenticate").click()
|
|
250
|
+
self.page.waitForLoadState("networkidle")""",
|
|
251
|
+
locator_tier=1,
|
|
252
|
+
semantic_docstring="Authenticates user through enterprise SSO portal with token and networkidle smart wait.",
|
|
253
|
+
verified_on_url="https://app.enterprise.com/auth/sso",
|
|
254
|
+
mutation_score=92.5,
|
|
255
|
+
tags="auth,login,signin,sso,security"
|
|
256
|
+
),
|
|
257
|
+
PageClassPrimitive(
|
|
258
|
+
skill_id="SKILL_INVOICE_APPROVE_RECONCILE",
|
|
259
|
+
page_class="InvoicePage",
|
|
260
|
+
method_signature="approveAndReconcileInvoice(invoice_code: str)",
|
|
261
|
+
method_code="""def approveAndReconcileInvoice(self, invoice_code: str):
|
|
262
|
+
self.page.getByRole("row", name=invoice_code).getByRole("button", name="Approve").click()
|
|
263
|
+
self.page.getByRole("button", name="Confirm Approval").click()
|
|
264
|
+
self.page.waitForSelector(".p-component-overlay", state="detached")""",
|
|
265
|
+
locator_tier=1,
|
|
266
|
+
semantic_docstring="Approves and reconciles invoice row item with modal overlay detachment verification.",
|
|
267
|
+
verified_on_url="https://app.enterprise.com/invoices",
|
|
268
|
+
mutation_score=88.0,
|
|
269
|
+
tags="invoice,billing,approve,reconcile,modal"
|
|
270
|
+
),
|
|
271
|
+
PageClassPrimitive(
|
|
272
|
+
skill_id="SKILL_TABLE_PAGINATION_NAV",
|
|
273
|
+
page_class="DataTableComponent",
|
|
274
|
+
method_signature="navigateToNextPage(expected_min_rows: int = 1)",
|
|
275
|
+
method_code="""def navigateToNextPage(self, expected_min_rows: int = 1):
|
|
276
|
+
next_btn = self.page.getByRole("button", name="Next Page")
|
|
277
|
+
if next_btn.is_enabled():
|
|
278
|
+
next_btn.click()
|
|
279
|
+
self.page.waitForSelector(".loading-shimmer", state="detached")
|
|
280
|
+
rows = self.page.getByRole("row").all()
|
|
281
|
+
assert len(rows) >= expected_min_rows, f"Expected at least {expected_min_rows} rows" """,
|
|
282
|
+
locator_tier=1,
|
|
283
|
+
semantic_docstring="Navigates through paginated grid/table with dynamic shimmer detachment and row count assertion.",
|
|
284
|
+
verified_on_url="https://app.enterprise.com/data-grid",
|
|
285
|
+
mutation_score=90.0,
|
|
286
|
+
tags="table,grid,pagination,paging,navigation"
|
|
287
|
+
),
|
|
288
|
+
PageClassPrimitive(
|
|
289
|
+
skill_id="SKILL_FORM_MODAL_SUBMIT",
|
|
290
|
+
page_class="UserManagementPage",
|
|
291
|
+
method_signature="fillAndSubmitModalForm(user_data: dict)",
|
|
292
|
+
method_code="""def fillAndSubmitModalForm(self, user_data: dict):
|
|
293
|
+
dialog = self.page.getByRole("dialog")
|
|
294
|
+
dialog.getByRole("textbox", name="Full Name").fill(user_data.get("name", ""))
|
|
295
|
+
dialog.getByRole("textbox", name="Email").fill(user_data.get("email", ""))
|
|
296
|
+
dialog.getByRole("combobox", name="Role").selectOption(user_data.get("role", "Viewer"))
|
|
297
|
+
dialog.getByRole("button", name="Save").click()
|
|
298
|
+
self.page.waitForSelector(".toast-success", state="visible")""",
|
|
299
|
+
locator_tier=1,
|
|
300
|
+
semantic_docstring="Fills interactive modal dialog form and validates submission toast notification.",
|
|
301
|
+
verified_on_url="https://app.enterprise.com/users",
|
|
302
|
+
mutation_score=87.5,
|
|
303
|
+
tags="modal,dialog,form,input,submit"
|
|
304
|
+
),
|
|
305
|
+
PageClassPrimitive(
|
|
306
|
+
skill_id="SKILL_ATTACHMENT_UPLOAD_VERIFY",
|
|
307
|
+
page_class="DocumentManagementPage",
|
|
308
|
+
method_signature="uploadAttachmentAndVerify(file_path: str, expected_filename: str)",
|
|
309
|
+
method_code="""def uploadAttachmentAndVerify(self, file_path: str, expected_filename: str):
|
|
310
|
+
file_input = self.page.locator("input[type='file']")
|
|
311
|
+
file_input.setInputFiles(file_path)
|
|
312
|
+
self.page.waitForSelector(".upload-progress", state="detached")
|
|
313
|
+
badge = self.page.getByText(expected_filename)
|
|
314
|
+
assert badge.is_visible(), f"Expected uploaded file badge '{expected_filename}' to be visible" """,
|
|
315
|
+
locator_tier=1,
|
|
316
|
+
semantic_docstring="Uploads file attachment, waits for progress bar detachment and asserts filename badge.",
|
|
317
|
+
verified_on_url="https://app.enterprise.com/documents",
|
|
318
|
+
mutation_score=94.0,
|
|
319
|
+
tags="upload,attachment,file,document,verify"
|
|
320
|
+
)
|
|
321
|
+
]
|
|
322
|
+
for s in samples:
|
|
323
|
+
vault.register_primitive(s)
|
|
324
|
+
print(f"✅ Seeded {len(samples)} verified PageClass primitives into SkillVault.")
|
|
325
|
+
return 0
|
|
326
|
+
|
|
327
|
+
if args.list:
|
|
328
|
+
items = vault.list_primitives()
|
|
329
|
+
print(json.dumps(items, indent=2))
|
|
330
|
+
return 0
|
|
331
|
+
|
|
332
|
+
if args.query or args.tag:
|
|
333
|
+
matches = vault.query_primitives(args.query or "", tag=args.tag)
|
|
334
|
+
print(f"🔍 Found {len(matches)} matching primitives in SkillVault (Fuzzy Semantic Search):")
|
|
335
|
+
print(json.dumps(matches, indent=2))
|
|
336
|
+
return 0
|
|
337
|
+
|
|
338
|
+
print("SkillVault active. Use --query, --tag, --list, or --seed-samples.")
|
|
339
|
+
return 0
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
if __name__ == "__main__":
|
|
343
|
+
sys.exit(main())
|