@sun-asterisk/sungen 3.2.10-beta.5 → 3.2.10

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 (45) hide show
  1. package/dist/capabilities/discover.d.ts.map +1 -1
  2. package/dist/capabilities/discover.js +2 -5
  3. package/dist/capabilities/discover.js.map +1 -1
  4. package/dist/cli/commands/capability.d.ts.map +1 -1
  5. package/dist/cli/commands/capability.js +2 -44
  6. package/dist/cli/commands/capability.js.map +1 -1
  7. package/dist/harness/capability.d.ts +0 -1
  8. package/dist/harness/capability.d.ts.map +1 -1
  9. package/dist/harness/capability.js.map +1 -1
  10. package/dist/harness/catalog/drivers.yaml +1 -2
  11. package/dist/harness/query-catalog.d.ts +2 -32
  12. package/dist/harness/query-catalog.d.ts.map +1 -1
  13. package/dist/harness/query-catalog.js +0 -0
  14. package/dist/harness/query-catalog.js.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/orchestrator/templates/ai-src/commands/run-test.md +0 -1
  20. package/dist/orchestrator/templates/ai-src/config/claude.md +1 -3
  21. package/dist/orchestrator/templates/ai-src/config/copilot.md +1 -3
  22. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +5 -15
  23. package/dist/orchestrator/templates/specs-db.d.ts +11 -201
  24. package/dist/orchestrator/templates/specs-db.d.ts.map +1 -1
  25. package/dist/orchestrator/templates/specs-db.js +91 -440
  26. package/dist/orchestrator/templates/specs-db.js.map +1 -1
  27. package/dist/orchestrator/templates/specs-db.ts +79 -463
  28. package/dist/orchestrator/templates/specs-test-data.ts +3 -39
  29. package/package.json +4 -8
  30. package/src/capabilities/discover.ts +2 -5
  31. package/src/cli/commands/capability.ts +2 -40
  32. package/src/harness/capability.ts +0 -1
  33. package/src/harness/catalog/drivers.yaml +1 -2
  34. package/src/harness/query-catalog.ts +0 -0
  35. package/src/index.ts +2 -2
  36. package/src/orchestrator/templates/ai-src/commands/run-test.md +0 -1
  37. package/src/orchestrator/templates/ai-src/config/claude.md +1 -3
  38. package/src/orchestrator/templates/ai-src/config/copilot.md +1 -3
  39. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +5 -15
  40. package/src/orchestrator/templates/specs-db.ts +79 -463
  41. package/src/orchestrator/templates/specs-test-data.ts +3 -39
  42. package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +0 -85
  43. package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +0 -101
  44. package/src/orchestrator/templates/ai-src/commands/create-data-test.md +0 -85
  45. package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +0 -101
@@ -48,27 +48,9 @@ export class TestDataLoader {
48
48
  if (value === undefined || value === null) {
49
49
  throw new Error(`Test data key not found: ${key}`);
50
50
  }
51
- // A projected array (e.g. `{{q.rows[*].name}}`) serializes as a JSON list so the full
52
- // ordered set is preserved for the assert-compare — String([...]) would flatten it to a
53
- // lossy comma-join that can't be distinguished from a scalar containing commas.
54
- if (Array.isArray(value)) return this.interpolate(JSON.stringify(value));
55
51
  return this.interpolate(String(value));
56
52
  }
57
53
 
58
- /**
59
- * Resolve a key to its RAW, uncoerced value (array/object/number kept as-is) — for binding
60
- * DB query parameters, where type-strict stores (MySQL numeric columns, MongoDB) require the
61
- * native type. get() stringifies for Gherkin text; raw() must not. Throws on missing, mirroring
62
- * get()'s guard.
63
- */
64
- raw(key: string): any {
65
- const value = this.resolve(key);
66
- if (value === undefined || value === null) {
67
- throw new Error(`Test data key not found: ${key}`);
68
- }
69
- return value;
70
- }
71
-
72
54
  /**
73
55
  * Substitute embedded `{{ref}}` cross-references inside a resolved string value.
74
56
  * Each ref is looked up via resolve() (flat key, @cases row column, or dotted path),
@@ -96,7 +78,6 @@ export class TestDataLoader {
96
78
  * `q.count` / `q.length` → number of rows
97
79
  * `q.first.col` / `q.last.col` / `q[2].col` → a specific row's column
98
80
  * `q.col` → shorthand for the first row's column
99
- * `q.rows[*].col` → the `col` of EVERY row, projected to an ordered array
100
81
  */
101
82
  private resolve(key: string): any {
102
83
  // 1. Exact flat key — captured vars (set()) live under a literal, possibly dotted, key.
@@ -107,26 +88,9 @@ export class TestDataLoader {
107
88
  return this.data[key];
108
89
  }
109
90
  // 2. Structured path: head from the row (cases) or shared data, then walk segments.
110
- // `[n]` `.n` (numeric index); `[*]` → `.*` (project the rest of the path over each element).
111
- const tokens = String(key).replace(/\[(\d+)\]/g, '.$1').replace(/\[\*\]/g, '.*').split('.');
112
- const cur: any = (this.row && tokens[0] in this.row) ? this.row[tokens[0]] : this.data[tokens[0]];
113
- return TestDataLoader.walk(cur, tokens, 1);
114
- }
115
-
116
- /**
117
- * Walk the remaining path tokens from `i`. A `*` token on an array projects: the rest of the
118
- * path is mapped over every element and the collected array returned (recurses for nested `[*]`).
119
- * Non-`*` tokens use the linear single-step navigation, so numeric/first/last/object access is
120
- * unchanged.
121
- */
122
- private static walk(cur: any, tokens: string[], i: number): any {
123
- for (; i < tokens.length && cur != null; i++) {
124
- if (tokens[i] === '*' && Array.isArray(cur)) {
125
- const rest = tokens.slice(i + 1);
126
- return cur.map((el) => TestDataLoader.walk(el, rest, 0));
127
- }
128
- cur = TestDataLoader.step(cur, tokens[i]);
129
- }
91
+ const tokens = String(key).replace(/\[(\d+)\]/g, '.$1').split('.');
92
+ let cur: any = (this.row && tokens[0] in this.row) ? this.row[tokens[0]] : this.data[tokens[0]];
93
+ for (let i = 1; i < tokens.length && cur != null; i++) cur = TestDataLoader.step(cur, tokens[i]);
130
94
  return cur;
131
95
  }
132
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "3.2.10-beta.5",
3
+ "version": "3.2.10",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -12,7 +12,7 @@
12
12
  "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && mkdir -p dist/generators/test-generator/adapters/appium/templates/steps && cp -r src/generators/test-generator/adapters/appium/templates/*.hbs dist/generators/test-generator/adapters/appium/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/appium/templates/steps dist/generators/test-generator/adapters/appium/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
13
13
  "build:dashboard": "cd ../../dashboard && npm install --silent && npm run build && cd - && cp ../../dashboard/dist/index.html src/dashboard/templates/index.html",
14
14
  "dev": "tsx src/cli/index.ts",
15
- "test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/exporter/delivery-preflight-selectors.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/runtime/test-data-array-projection.run.ts &&tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts && tsx tests/db-runtime/sql-placeholder-rewrite.run.ts && tsx tests/db-runtime/fallback-flow-codified.run.ts && tsx tests/db-runtime/cosmos-engine.run.ts && tsx tests/db-runtime/mongodb-engine.run.ts && tsx tests/db-runtime/dynamodb-engine.run.ts && tsx tests/db-runtime/mysql-integration.opt-in.run.ts && tsx tests/db-runtime/mongodb-integration.opt-in.run.ts && tsx tests/db-runtime/dynamodb-integration.opt-in.run.ts",
15
+ "test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts && tsx tests/ingest/run.ts && tsx tests/eval/run.ts && tsx tests/exporter/run.ts && tsx tests/exporter/feature-parser-category.run.ts && tsx tests/exporter/api-detail-sheet.run.ts && tsx tests/exporter/overview-sheet.run.ts && tsx tests/exporter/delivery-divider-tcid.run.ts && tsx tests/exporter/note-expected-actual.run.ts && tsx tests/exporter/test-data-vars.run.ts && tsx tests/exporter/mobile-app-id.run.ts && tsx tests/exporter/api-testcase-cells.run.ts && tsx tests/exporter/delivery-cases-result.run.ts && tsx tests/exporter/precondition-auth-role.run.ts && tsx tests/exporter/report-builder-parity.run.ts && tsx tests/exporter/delivery-preflight-testdata.run.ts && tsx tests/exporter/delivery-preflight-selectors.run.ts && tsx tests/dashboard/api-flows-discovery.run.ts && tsx tests/dashboard/status-results-fallback.run.ts && tsx tests/codegen/verb-ref-masking.run.ts && tsx tests/codegen/table-aria-hidden-locators.run.ts && tsx tests/api-runtime/base-path-url-join.run.ts && tsx tests/api-runtime/file-upload-multipart.run.ts && tsx tests/api-runtime/schema-assertion.run.ts && tsx tests/runtime/test-data-cross-ref.run.ts && tsx tests/capabilities/run.ts && tsx tests/openapi/run.ts && tsx tests/api-field-coverage/run.ts && tsx tests/packaging/run.ts && tsx tests/generate-hint/run.ts && tsx tests/template-assertion/run.ts && tsx tests/journey/run.ts && tsx tests/harness/serial-cascade.run.ts && tsx tests/ai-skills/cross-assistant-orphan.run.ts && tsx tests/init/codex-mcp-config.run.ts && tsx src/orchestrator/ai-skills/golden-skills.test.ts && tsx tests/db-runtime/sql-placeholder-rewrite.run.ts && tsx tests/db-runtime/fallback-flow-codified.run.ts && tsx tests/db-runtime/mysql-integration.opt-in.run.ts",
16
16
  "test:update": "tsx tests/golden/run.ts --update && tsx tests/audit/run.ts --update && tsx tests/ingest/run.ts --update",
17
17
  "prepublishOnly": "npm run build:dashboard && npm run build"
18
18
  },
@@ -39,8 +39,7 @@
39
39
  "@babel/types": "^7.28.5",
40
40
  "@cucumber/gherkin": "^37.0.0",
41
41
  "@cucumber/messages": "^31.0.0",
42
- "@sungen/driver-data-factory": "3.2.10-beta.5",
43
- "@sungen/driver-ui": "3.2.10-beta.5",
42
+ "@sungen/driver-ui": "3.2.10",
44
43
  "chalk": "^5.6.2",
45
44
  "commander": "^14.0.2",
46
45
  "dotenv": "^17.2.3",
@@ -66,9 +65,6 @@
66
65
  "LICENSE"
67
66
  ],
68
67
  "devDependencies": {
69
- "mysql2": "^3",
70
- "mongodb": "^7",
71
- "@aws-sdk/client-dynamodb": "^3",
72
- "@aws-sdk/lib-dynamodb": "^3"
68
+ "mysql2": "^3"
73
69
  }
74
70
  }
@@ -27,7 +27,7 @@ import { LOCAL_DRIVERS, registerCoreCapability } from './builtins';
27
27
  * move to their packages (R5.5/R5.6) they join this list and leave `LOCAL_DRIVERS`; eventually this
28
28
  * becomes a scan of installed `@sungen/driver-*` packages (R5.7).
29
29
  */
30
- const EXTERNAL_DRIVERS = ['@sungen/driver-ui', '@sungen/driver-db', '@sungen/driver-api', '@sungen/driver-mobile', '@sungen/driver-data-factory'];
30
+ const EXTERNAL_DRIVERS = ['@sungen/driver-ui', '@sungen/driver-db', '@sungen/driver-api', '@sungen/driver-mobile'];
31
31
 
32
32
  /**
33
33
  * Bundled (non-opt-in) drivers: shipped as a core dependency (`drivers.yaml` `bundled: true`), so the
@@ -36,10 +36,7 @@ const EXTERNAL_DRIVERS = ['@sungen/driver-ui', '@sungen/driver-db', '@sungen/dri
36
36
  * symlink for the package is missing). Surface it LOUDLY: otherwise the entire UI step vocabulary is
37
37
  * absent and every step silently compiles to "Unrecognized step pattern" with no clue why.
38
38
  */
39
- // `@sungen/driver-data-factory` is bundled too — but it is RUNTIME-FREE (only a knowledge catalog +
40
- // codegen), so shipping it by default adds no runtime, unlike ui/db/api/mobile. It makes standardized
41
- // test-data available out of the box (no `capability add` needed); inert until a field-map/recipe exists.
42
- const BUNDLED_DRIVERS = new Set(['@sungen/driver-ui', '@sungen/driver-data-factory']);
39
+ const BUNDLED_DRIVERS = new Set(['@sungen/driver-ui']);
43
40
 
44
41
  function loadExternalDriver(name: string): void {
45
42
  // Resolve from the user's PROJECT first, then from core's own location. Opt-in drivers
@@ -15,36 +15,6 @@ function coreVersion(): string {
15
15
  try { return require('../../../package.json').version || 'latest'; } catch { return 'latest'; }
16
16
  }
17
17
 
18
- /**
19
- * Resolve the driver version to install for the running core `core`.
20
- *
21
- * Lockstep wants the EXACT `core` version. But the beta channel publishes `<core>-beta.N` before a
22
- * stable `<core>` ever exists — so a released core (e.g. 3.2.10) asking for `@3.2.10` hits ETARGET
23
- * out-of-the-box when npm only has `3.2.10-beta.2` (`sungen capability add` then fails for a fresh
24
- * user). Fall back on the SAME version line: prefer exact `core`, else the highest `core-beta.N`,
25
- * else the `beta` dist-tag as a last resort. Keeps the driver's pinned core-dep on the same line.
26
- */
27
- function resolveDriverVersion(pkg: string, core: string): string {
28
- if (core === 'latest') return 'beta';
29
- try {
30
- const out = spawnSync('npm', ['view', pkg, 'versions', '--json'], { encoding: 'utf-8', shell: true });
31
- if (out.status !== 0 || !out.stdout) return core; // can't query → let the exact install try/fail
32
- let versions: string[] = [];
33
- const parsed = JSON.parse(out.stdout);
34
- versions = Array.isArray(parsed) ? parsed : [parsed];
35
- if (versions.includes(core)) return core; // exact exists → lockstep
36
- const prefix = `${core}-beta.`;
37
- const betas = versions
38
- .filter((v) => typeof v === 'string' && v.startsWith(prefix))
39
- .map((v) => parseInt(v.slice(prefix.length), 10))
40
- .filter((n) => Number.isInteger(n));
41
- if (betas.length) return `${core}-beta.${Math.max(...betas)}`; // highest same-line prerelease
42
- return 'beta'; // nothing on this line → newest beta dist-tag
43
- } catch {
44
- return core;
45
- }
46
- }
47
-
48
18
  /**
49
19
  * Add `test:mobile` to the project's package.json scripts (if absent).
50
20
  * Idempotent — skips if the script is already set.
@@ -217,22 +187,14 @@ export function registerCapabilityCommand(program: Command): void {
217
187
  }
218
188
 
219
189
  const adapterName = meta.adapter || meta.id;
220
- // Bundled = a platform adapter baked into core (e.g. `web`), OR a capability driver shipped
221
- // as a core dependency (`drivers.yaml` bundled: true, e.g. data-factory) — present already,
222
- // so no install/verify: `capability add` on it just records it in the profile.
223
- const bundled = adapterRegistry.hasAdapter(adapterName) || meta.bundled === true;
190
+ const bundled = adapterRegistry.hasAdapter(adapterName); // e.g. `web` is bundled in Phase 2a
224
191
 
225
192
  if (!bundled && !o.skipInstall) {
226
193
  // Pin the driver to the RUNNING core's exact version (lockstep). Without this, plain
227
194
  // `npm install @sungen/driver-x` resolves the package's `latest` dist-tag — which on a
228
195
  // beta channel can lag (e.g. a prerelease first-published-as-latest), pulling a driver
229
196
  // whose core dep resolves to the wrong @sun-asterisk/sungen and fails to load.
230
- const core = coreVersion();
231
- const target = resolveDriverVersion(meta.package, core); // exact core, else same-line beta, else 'beta' tag
232
- const spec = `${meta.package}@${target}`;
233
- if (target !== core) {
234
- console.log(`ℹ ${meta.package}@${core} is not published yet — installing ${spec} (closest on the ${core} line).`);
235
- }
197
+ const spec = `${meta.package}@${coreVersion()}`;
236
198
  console.log(`📦 Installing ${spec} (dev dependency)...`);
237
199
  const r = spawnSync('npm', ['install', '-D', spec], { stdio: 'inherit', shell: true });
238
200
  if (r.status !== 0) throw new Error(`npm install -D ${spec} failed.`);
@@ -24,7 +24,6 @@ export interface DriverMeta {
24
24
  package: string;
25
25
  runtime?: string;
26
26
  adapter?: string; // registry adapter name (defaults to id)
27
- bundled?: boolean; // shipped as a core dependency — present without `capability add`
28
27
  capabilities: string[];
29
28
  unblocks?: string[];
30
29
  }
@@ -51,8 +51,7 @@ drivers:
51
51
  data-factory:
52
52
  kind: capability
53
53
  package: "@sungen/driver-data-factory"
54
- status: shipped
55
- bundled: true # runtime-free knowledge+codegen — shipped by default (no `capability add` needed)
54
+ status: planned
56
55
  capabilities: ["@dataFactory"]
57
56
  unblocks: [M1]
58
57
  mock:
Binary file
package/src/index.ts CHANGED
@@ -25,8 +25,8 @@ export { getPathCode, inferPath, resolvePathVariables } from './generators/test-
25
25
  export { parseQueryOverrides } from './harness/annotation-overrides';
26
26
 
27
27
  // --- Named-query catalog (shared: the DB driver's codegen + core's data-driven advisory lint) ---
28
- export { resolveQuery, compileQuery, compileShape, isShapeEntry, lintCatalog } from './harness/query-catalog';
29
- export type { QueryEntry, MongoFind, MongoPipeline } from './harness/query-catalog';
28
+ export { resolveQuery, compileQuery, lintCatalog } from './harness/query-catalog';
29
+ export type { QueryEntry } from './harness/query-catalog';
30
30
 
31
31
  // --- Shared harness: viewpoint catalog + coverage gate / assertion depth ---
32
32
  // (the UI capability's gateProvider composes these; they also back core's ingest + audit fallback)
@@ -127,7 +127,6 @@ If the unit is **api-first**, skip every selector/capture phase (an API test has
127
127
  ## Pre-run (phased — per `sungen-selector-fix` skill)
128
128
 
129
129
  1. Verify `<base>/<name>/` has `.feature` + `test-data.yaml`. **If the feature has `@query` steps**, check the resolved datasource `engine` in `qa/datasources.yaml` — unsupported engine → follow the `sungen-gherkin-syntax` skill § "Unsupported DB engine — fallback" before compiling (do not attempt a direct connect).
130
- 1.5. **Data Factory pre-check (before running)** — if `data-factory` is enabled in `qa/capabilities.yaml` **and** a field-map exists (`qa/data-factory/<name>.fields.yaml`), confirm the test-data is standardized and covers each field before you run: `[ -x ./bin/sungen.js ] && ./bin/sungen.js data lint --screen <name> || npx sungen data lint --screen <name>`. If it reports coverage gaps (missing boundary/invalid, unresolved values, unmapped error codes) or the field-map is newer than `test-data/<name>.yaml`, refresh via `/sungen:create-data-test <name>` (or `sungen data gen --screen <name>`) — then compile. Blind spots (a `type` not in the catalog) are for the QA to answer, not to invent. No field-map / driver disabled → skip this step.
131
130
  2. **Phase 0 — Selector Pre-gen**: if `selectors.yaml` is missing/empty or doesn't cover the feature file's `[Reference]`s, apply the following decision tree before running Phase 0 from `sungen-selector-fix`:
132
131
 
133
132
  ```
@@ -18,16 +18,14 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
18
18
  | `sungen-delivery` | Export Gherkin + Playwright results → CSV test case deliverable |
19
19
  | `sungen-capture` | Acquire visual/design context — one skill, 4 modes: figma-mcp (Dev Mode MCP), figma-pat (URL → spec_figma.md), live (Playwright MCP scan), local (images in `requirements/ui/`) |
20
20
  | `sungen-locale` | Bootstrap i18n — audit selectors, detect locale switch mechanism, generate test-data overlay |
21
- | `sungen-data-factory` | Standardized test-data — field-type/security catalog, 4-source method, `.overwrite`, blind-spot prompts |
22
21
 
23
- ## Workflow (8 AI commands)
22
+ ## Workflow (7 AI commands)
24
23
 
25
24
  | Command | What it does |
26
25
  |---|---|
27
26
  | `/sungen:add-screen <name> <path>` | Scaffold `qa/screens/<name>/` directories |
28
27
  | `/sungen:add-flow <name> [--path <url>]` | Scaffold `qa/flows/<name>/` directories for E2E cross-screen testing |
29
28
  | `/sungen:create-test <name>` | Generate `.feature` + `test-data.yaml` (auto-detects screen or flow) |
30
- | `/sungen:create-data-test <name>` | Generate standardized test-data (valid/boundary/invalid + CHK trace) from the Data Factory catalog; no name = all units (asks to confirm) |
31
29
  | `/sungen:review <name>` | Score syntax, coverage, viewpoint quality (auto-detects screen or flow) |
32
30
  | `/sungen:run-test <name>` | Generate `selectors.yaml`, compile, run, auto-fix (auto-detects screen or flow) |
33
31
  | `/sungen:delivery [name...]` | Export test cases → CSV for QA delivery (all screens if no arg) |
@@ -18,16 +18,14 @@ You generate 3 files for sungen — a Gherkin compiler that produces Playwright
18
18
  | `sungen-delivery` | Export Gherkin + Playwright results → CSV test case deliverable |
19
19
  | `sungen-capture` | Acquire visual/design context — one skill, 4 modes: figma-mcp (Dev Mode MCP), figma-pat (URL → spec_figma.md), live (Playwright MCP scan), local (images in `requirements/ui/`) |
20
20
  | `sungen-locale` | Bootstrap i18n — audit selectors, detect locale switch mechanism, generate test-data overlay |
21
- | `sungen-data-factory` | Standardized test-data — field-type/security catalog, 4-source method, `.overwrite`, blind-spot prompts |
22
21
 
23
- ## Workflow (8 AI commands)
22
+ ## Workflow (7 AI commands)
24
23
 
25
24
  | Command | What it does |
26
25
  |---|---|
27
26
  | `/sungen-add-screen <name> <path>` | Scaffold `qa/screens/<name>/` directories |
28
27
  | `/sungen-add-flow <name> [--path <url>]` | Scaffold `qa/flows/<name>/` directories for E2E cross-screen testing |
29
28
  | `/sungen-create-test <name>` | Generate `.feature` + `test-data.yaml` (auto-detects screen or flow) |
30
- | `/sungen-create-data-test <name>` | Generate standardized test-data (valid/boundary/invalid + CHK trace) from the Data Factory catalog; no name = all units (asks to confirm) |
31
29
  | `/sungen-review <name>` | Score syntax, coverage, viewpoint quality (auto-detects screen or flow) |
32
30
  | `/sungen-run-test <name>` | Generate `selectors.yaml`, compile, run, auto-fix (auto-detects screen or flow) |
33
31
  | `/sungen-delivery [name...]` | Export test cases → CSV for QA delivery (all screens if no arg) |
@@ -143,24 +143,14 @@ Scenario: ...
143
143
 
144
144
  Path access on a bound result: `{{q.count}}`/`{{q.length}}`, `{{q.first.col}}`, `{{q.last.col}}`, `{{q[2].col}}`, `{{q.col}}` (= first row's col). `expect A is B` also supports `is at least` / `is at most` / `is not`. Tier-2 declarative (trivial inline, no catalog): `User see [<table>] row where [<col>] is {{v}} [has [<col2>] = "x"]`, `… no row where …`, `… count is {{n}}`. Full grammar + catalog/datasource/secret rules → **Advanced → Database** doc. Only emit DB steps when the project has a `database/` catalog / `datasources.yaml`.
145
145
 
146
- ### NoSQL engines (Cosmos, MongoDB, DynamoDB) — natively supported
147
-
148
- Beyond the three SQL engines, the Data Driver has native NoSQL engines. Declarative steps (`User see [table] row where …`, `… no row …`, `… count is …`) work across ALL of them; only the raw `@query` catalog shape differs per engine:
149
-
150
- - **`engine: cosmos`** (Azure Cosmos DB, NoSQL API) — SQL dialect. Author `sql:` catalog entries against the mandatory `c` alias (`SELECT * FROM c WHERE c.<field> = :p`); named params bind automatically. Config: `endpoint`+`key` (or a connection-string `url`), `database`, `container` (default when no `[table]`). Read-only by construction (the query API can't mutate).
151
- - **`engine: mongodb`** — no SQL. Author a `find:` (`{collection, filter, projection?, sort?, limit?}`) or `pipeline:` (`{collection, stages: […]}`) catalog entry; `:param` placeholders bind at runtime. `$out`/`$merge`/`$where`/`$function`/`$accumulator`/`mapReduce` are refused. Config: a `mongodb://` `url`. Read-only = a server-side read role.
152
- - **`engine: dynamodb`** — raw `@query` is PartiQL SELECT (`sql: SELECT … FROM "table" WHERE pk = :p`). Declarative steps route GetItem/Query only for partition-/sort-key filters via a `keySchema:` registry; a **non-key filter is refused** (use a raw PartiQL SELECT — never a full-table Scan). Config: `region` + `keySchema` (credentials from the AWS env chain). Read-only = IAM `PartiQLSelect`-only.
153
-
154
- Full config + catalog examples → **Advanced → Database** doc.
155
-
156
146
  ### Unsupported DB engine — fallback (ask, don't improvise)
157
147
 
158
- Direct SQL verification (`@query`, the Data Driver) covers these SQL engines: **`{postgres, sqlite, mysql}`** — plus the three native NoSQL engines above (Cosmos, MongoDB, DynamoDB). When a datasource's `engine` (or the DB the user describes) is **outside all of those** — e.g. Cassandra, Oracle, Neo4j, a proprietary store:
148
+ Direct SQL verification (`@query`, the Data Driver) only supports these engines: **`{postgres, sqlite, mysql}`**. When a datasource's `engine` (or the DB the user describes) is **outside this set** — e.g. Cosmos NoSQL, MongoDB, Cassandra, DynamoDB:
159
149
 
160
150
  1. **Do not** attempt a direct connect. **Do not** improvise a verification method.
161
151
  2. Present a fixed `AskUserQuestion` with exactly these 3 branches (always these, no others invented):
162
152
  - **@api** — verify state via the project's internal API (route to the API driver).
163
- - **@manual** — emit a `@manual` step + manual-check guidance (e.g. the vendor console / CLI — `cqlsh` for Cassandra, `cypher-shell` for Neo4j).
153
+ - **@manual** — emit a `@manual` step + manual-check guidance (e.g. Cosmos Data Explorer, `az cosmosdb`, the vendor CLI).
164
154
  - **Proxy / read-replica SQL** — if the user can supply a SQL-compatible endpoint (including Azure Cosmos DB **for PostgreSQL** / Citus), treat it as `engine: postgres` (or `mysql`) and use the normal Data Driver path above.
165
155
  3. **Recommend, don't decide.** Check the project for signals and put the best-fit branch **first**, tagged **"(Recommended)"** with a one-line reason:
166
156
  - a project API driver / `qa/api/` catalog / `PEERCONNE_URL`-style internal API exists → **@api** first;
@@ -173,11 +163,11 @@ Direct SQL verification (`@query`, the Data Driver) covers these SQL engines: **
173
163
  Example:
174
164
  ```
175
165
  AskUserQuestion:
176
- question: "This datasource's engine (cassandra) isn't a supported Data Driver engine ({postgres, sqlite, mysql} + Cosmos/MongoDB/DynamoDB) — direct connect isn't supported. How should DB state be verified?"
166
+ question: "This datasource's engine (cosmos-nosql) isn't in {postgres, sqlite, mysql} — direct SQL connect isn't supported. How should DB state be verified?"
177
167
  options:
178
168
  - "@api — verify via the project's internal API (Recommended: qa/api/ catalog found)"
179
- - "@manual — emit a @manual step + cqlsh check"
180
- - "Proxy / read-replica SQL — supply a SQL-compatible endpoint → engine: postgres"
169
+ - "@manual — emit a @manual step + Cosmos Data Explorer / az cosmosdb check"
170
+ - "Proxy / read-replica SQL — supply a SQL-compatible endpoint (e.g. Cosmos DB for PostgreSQL) → engine: postgres"
181
171
  - "Other — describe your own verification approach"
182
172
  ```
183
173