@sun-asterisk/sungen 3.2.1-beta.1 → 3.2.2-beta.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.
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +31 -0
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/depth-lint.d.ts +3 -0
- package/dist/cli/commands/depth-lint.d.ts.map +1 -0
- package/dist/cli/commands/depth-lint.js +88 -0
- package/dist/cli/commands/depth-lint.js.map +1 -0
- package/dist/cli/commands/gate.d.ts +3 -0
- package/dist/cli/commands/gate.d.ts.map +1 -0
- package/dist/cli/commands/gate.js +83 -0
- package/dist/cli/commands/gate.js.map +1 -0
- package/dist/cli/commands/journey.d.ts +3 -0
- package/dist/cli/commands/journey.d.ts.map +1 -0
- package/dist/cli/commands/journey.js +96 -0
- package/dist/cli/commands/journey.js.map +1 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +25 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +59 -0
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/types.d.ts +38 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +31 -2
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +144 -1
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/harness/depth-lint.d.ts +25 -0
- package/dist/harness/depth-lint.d.ts.map +1 -0
- package/dist/harness/depth-lint.js +118 -0
- package/dist/harness/depth-lint.js.map +1 -0
- package/dist/harness/journey.d.ts +68 -0
- package/dist/harness/journey.d.ts.map +1 -0
- package/dist/harness/journey.js +328 -0
- package/dist/harness/journey.js.map +1 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +4 -1
- package/dist/harness/parse.js.map +1 -1
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +1 -0
- package/dist/orchestrator/ai-rules-updater.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-agent-generator.md +44 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +24 -1
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +3 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-delivery.md +27 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -3
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +3 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +4 -1
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +3 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +27 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +22 -3
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +2 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +3 -0
- package/dist/orchestrator/templates/specs-api.d.ts +7 -0
- package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-api.js +13 -2
- package/dist/orchestrator/templates/specs-api.js.map +1 -1
- package/dist/orchestrator/templates/specs-api.ts +13 -2
- package/package.json +3 -3
- package/src/cli/commands/delivery.ts +32 -2
- package/src/cli/commands/depth-lint.ts +51 -0
- package/src/cli/commands/gate.ts +44 -0
- package/src/cli/commands/journey.ts +59 -0
- package/src/cli/index.ts +6 -0
- package/src/exporters/feature-parser.ts +57 -0
- package/src/exporters/types.ts +38 -0
- package/src/exporters/xlsx-exporter.ts +176 -2
- package/src/harness/depth-lint.ts +122 -0
- package/src/harness/journey.ts +333 -0
- package/src/harness/parse.ts +4 -1
- package/src/orchestrator/ai-rules-updater.ts +1 -0
- package/src/orchestrator/templates/ai-instructions/claude-agent-generator.md +44 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +24 -1
- package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +3 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-delivery.md +27 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +22 -3
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +3 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +4 -1
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +3 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +27 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +22 -3
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +2 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +3 -0
- package/src/orchestrator/templates/specs-api.ts +13 -2
|
@@ -88,6 +88,33 @@ Multi-locale (no `SUNGEN_ENV`): one **`<LOCALE> Auto`** sheet per locale + a sin
|
|
|
88
88
|
|
|
89
89
|
---
|
|
90
90
|
|
|
91
|
+
## API delivery — extra worksheet
|
|
92
|
+
|
|
93
|
+
For **api-kind units** (`qa/api/<area>/`), the `.xlsx` gains a third worksheet **`API detail`** (appended after Auto/Manual). The main BM-2-901-13 Testcases layout is unchanged. The CSV is unchanged (16-column, no extra sheet).
|
|
94
|
+
|
|
95
|
+
### Required sources (API detail sheet only)
|
|
96
|
+
|
|
97
|
+
| Source | Path | Created by |
|
|
98
|
+
|--------|------|------------|
|
|
99
|
+
| Endpoint catalog | `qa/api/<area>/api/apis.yaml` | `sungen add --api` or `sungen api import` |
|
|
100
|
+
| Scenario annotations | `qa/api/<area>/features/<feature>.feature` | `create-test` |
|
|
101
|
+
|
|
102
|
+
### API detail column mapping
|
|
103
|
+
|
|
104
|
+
| Column | Source |
|
|
105
|
+
|--------|--------|
|
|
106
|
+
| Endpoint | `path` from `apis.yaml` catalog entry |
|
|
107
|
+
| Method | `method` from catalog entry (uppercased) |
|
|
108
|
+
| Auth / Datasource | catalog `datasource` + any `@auth:<role>` tag from scenarios calling this endpoint |
|
|
109
|
+
| Request shape | catalog `body` + `params` fields composed as `body: {…}; params: [a, b]` |
|
|
110
|
+
| Expected-status matrix | `@cases:<dataset>` label for data-driven scenarios; catalog `expect.status` as fallback |
|
|
111
|
+
| Flow steps | Ordered `@api:<name>` call chain from multi-call scenarios (e.g. `register → count_users`) |
|
|
112
|
+
| Concurrency invariant | `@concurrent:<N>` + `@query:<oracle>` from concurrent scenarios (e.g. `ok_count=2; @query user_count`) |
|
|
113
|
+
|
|
114
|
+
**Sources are catalog + annotations only** — Field Metadata (FM) is not required for this sheet.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
91
118
|
## Excluded from CSV
|
|
92
119
|
|
|
93
120
|
- `@steps:<name>` **base** scenarios — these are setup-only, inlined into `@extend:...` scenarios at compile time
|
|
@@ -21,6 +21,23 @@ Then choose the fix from the patterns below.
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
+
## ⛔ Source of truth — classify EVERY failure before you "fix" it
|
|
25
|
+
|
|
26
|
+
`.feature` + `test-data.yaml` + `spec.md` are the **oracle**. The **live page is NOT** — it may be the thing that's broken. A failing test is not automatically a test to "make pass". Classify first:
|
|
27
|
+
|
|
28
|
+
- **Selector-resolution failure** (element not found / wrong locator / strict-mode / wrong element type) → the test looked in the wrong place. **Fix the locator in `selectors.yaml`** (re-snapshot, copy the exact accessible name). Legit auto-fix.
|
|
29
|
+
- **Assertion-value failure** (element FOUND, but observed value ≠ expected) → STOP and ask: *is the TEST wrong, or is the APP wrong?*
|
|
30
|
+
- Expected value/rule is wrong **relative to `spec.md`** (typo, stale test-data) → fix `test-data.yaml`/`.feature` so it matches the **spec** — never the live page.
|
|
31
|
+
- App behaviour contradicts `spec.md` (spec says X, app shows Y) → **CANDIDATE BUG**. **Report it** (let the test FAIL / surface to the QA in the run summary). **NEVER** change the expected value, loosen the rule, weaken the assertion (`toHaveText`→`toContainText` to dodge a mismatch), edit `.feature`, or edit the generated `.spec.ts` to make it pass.
|
|
32
|
+
|
|
33
|
+
> **Cardinal sin (do NOT do this):** a `password > 8 chars` rule fails on a 6-char input → "fix" it to `>= 6` so the test passes. The logic is now meaningless. A failing assertion is a **finding**, not a chore.
|
|
34
|
+
|
|
35
|
+
**Auto-fix loop scope:** the run-test auto-fix loop engages ONLY on **selector-resolution** failures. On an assertion-value failure where the app contradicts the spec → **HALT and report**, do not loop it into passing.
|
|
36
|
+
|
|
37
|
+
**Never hand-edit the generated `.spec.ts`** (e.g. inserting `page.evaluate`/`fetch` to bypass a broken control). `sungen script-check` regenerates the spec from `.feature` and flags any edit as DRIFT — regenerate, don't patch.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
24
41
|
## Fix Priority (try in order)
|
|
25
42
|
|
|
26
43
|
1. **Auth issue** — page redirected to login? Fix auth first, everything else is noise
|
|
@@ -43,11 +60,13 @@ Then choose the fix from the patterns below.
|
|
|
43
60
|
| not a select | Custom dropdown, not native `<select>` | Set `variant: 'custom'` |
|
|
44
61
|
| Frame not found | iframe selector wrong or doesn't exist | Fix `frame` value, verify iframe in snapshot |
|
|
45
62
|
|
|
46
|
-
### Assertion errors →
|
|
63
|
+
### Assertion errors → apply the Source-of-truth gate above FIRST
|
|
47
64
|
|
|
48
|
-
|
|
65
|
+
> The "Fix" column below applies **only when the expected value was wrong relative to `spec.md`** (a test defect). If the app's value contradicts the spec, the row is a **candidate bug → report it, do not edit the expected to match live**. Never weaken `toHaveText`→`toContainText` just to pass.
|
|
66
|
+
|
|
67
|
+
| Error | Diagnosis | Fix (only if the TEST was wrong per spec) |
|
|
49
68
|
|---|---|---|
|
|
50
|
-
| toHaveText mismatch | Expected text differs from actual |
|
|
69
|
+
| toHaveText mismatch | Expected text differs from actual | If the test's expected was wrong per spec → fix value in test-data. If element is input type → change Gherkin type to `field`/`textarea` (triggers `toHaveValue`). If the app value contradicts spec → **report as bug**. |
|
|
51
70
|
| toHaveValue mismatch | Expected value differs from actual | Fix value in test-data |
|
|
52
71
|
| toContainText mismatch | Partial text not found | Fix expected partial text in test-data |
|
|
53
72
|
| toBeVisible timeout | Element exists but hidden, or name wrong | Check: is element conditionally visible? Wrong name? Inside dialog? |
|
|
@@ -214,6 +214,8 @@ Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `colu
|
|
|
214
214
|
| `@cases:dataset` | Data-driven: run the scenario once per row of the `dataset` LIST in test-data → one `test()` per row |
|
|
215
215
|
| `@query:name` | Database: run the named query from `database/queries.yaml` (precondition) and bind its rows to `{{name}}`; assert with `expect {{name.count}} …` + path access. Override params `@query:name(p={{v}})`. Repeatable. (Optional Data Driver — see Database verification above) |
|
|
216
216
|
| `@api:name` | API: run the named request from `api/apis.yaml` (precondition) and bind the response to `{{name}}`; assert with `expect {{name.status}} …` + path access (`{{name.body.<path>}}`). Override params `@api:name(p={{v}})`. Repeatable. (Optional API Driver) |
|
|
217
|
+
| `@concurrent:N` | API idempotency: fire the bound `@api` request N times in parallel, then bind aggregates on the `@api` name — `{{name.ok_count}}` (2xx count) and `{{name.status_counts}}` (status→count map). Assert the exactly-once invariant (`expect {{name.ok_count}} is 1`); pair with `@query` as a DB oracle. Tag order = run order: `@api` (mutate) before `@query` (verify). (Optional API Driver) |
|
|
218
|
+
| `@hybrid` | One unit, two capabilities: a signed-in browser session (UI) authorizes the `@api` call — the API request reuses the UI `storageState`. (Optional API + UI Drivers) |
|
|
217
219
|
|
|
218
220
|
### Data-driven scenarios (`@cases`)
|
|
219
221
|
|
|
@@ -9,6 +9,8 @@ user-invocable: false
|
|
|
9
9
|
- **Write incrementally — never emit the whole suite in one response.** Build the `.feature` in batches via successive `Write`/`Edit` (≈10–15 scenarios per call). For **Full coverage**, write tier-by-tier: `Write` Tier 1 → `Edit` append Tier 2 → `Edit` append Tier 3.
|
|
10
10
|
→ One huge `Write` can exceed the model's output-token cap → `API Error: Claude's response exceeded the N output token maximum`. Single-pass full coverage only fits when `CLAUDE_CODE_MAX_OUTPUT_TOKENS ≥ 64000`; otherwise batch. Batching also lets the audit/reviewer run per batch — higher quality.
|
|
11
11
|
|
|
12
|
+
- **Generate group-by-group (sequential here).** Copilot has no sub-agents, so generate one viewpoint group/theme at a time, tier-by-tier, keeping each `VP-` theme in its own id prefix. (The Claude Code variant fans these out as parallel `sungen-generator` shards and merges — same output shape, just no speedup. Keep each theme self-contained so it would merge cleanly either way.)
|
|
13
|
+
|
|
12
14
|
- `spec_figma.md` exists → read file only, **NEVER** call `mcp__figma__*`
|
|
13
15
|
→ PAT auth flow already done by `sungen-capture` (mode figma-pat); re-calling fails or duplicates work.
|
|
14
16
|
|
|
@@ -273,6 +275,7 @@ Security: [S1 – admin only]
|
|
|
273
275
|
|
|
274
276
|
**Depth is a GATE dimension (harness-roadmap P1) — self-raise, never silently go shallow:**
|
|
275
277
|
- For every data-correctness theme the catalog marks `depth.requires: data-assertion`, emit its `depth.template` shape by **default** — don't wait for the repair loop. `sungen audit` measures `businessDepth` (ratio of these scenarios that assert data) against an intent threshold (functional ≥ 0.70); below it the **gate FAILs**.
|
|
278
|
+
- **Verify depth deterministically before the gate:** run `sungen depth-lint --screen <name>`. It classifies every shallow business-critical scenario into **deepen-in-place** (add the theme's value assertion — the printed `template` is a hint, fit it to the actual claim) vs **cross-screen** (route to a flow / `@manual:Mx`). Clear the `deepen` list first — this is the mechanical way to hit `businessDepth` on the first pass instead of churning repair rounds. Never fake a value assertion onto a visibility/behavior scenario the lint over-counts; leave it and note the over-count.
|
|
276
279
|
- `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human) or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
|
|
277
280
|
- **Pick the right `@manual:Mx` code — it decides which driver can later automate the case** (`sungen audit` flags a code↔reason mismatch). Tag the code that matches the **oracle the reason describes**:
|
|
278
281
|
|
|
@@ -49,6 +49,17 @@ function substitute(text: string, params: Record<string, any>): string {
|
|
|
49
49
|
return text.replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, (_m, p) => encodeURIComponent(String(params[p] ?? '')));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Join a datasource base URL with a catalog path. Concatenate rather than rely on Playwright's
|
|
54
|
+
* baseURL resolution: an absolute path (`/user/1`) resolves against the base ORIGIN and would drop
|
|
55
|
+
* a base path component (`/api/v3`). Most APIs are mounted under such a prefix, so the full URL must
|
|
56
|
+
* be built explicitly.
|
|
57
|
+
*/
|
|
58
|
+
export function joinApiUrl(base: string, urlPath: string): string {
|
|
59
|
+
const b = base.replace(/\/$/, '');
|
|
60
|
+
return urlPath.startsWith('/') ? b + urlPath : `${b}/${urlPath}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
class ApiClient {
|
|
53
64
|
private configs: Record<string, ApiDataSource> | null = null;
|
|
54
65
|
|
|
@@ -103,13 +114,13 @@ class ApiClient {
|
|
|
103
114
|
// `storageState` (the @auth role's saved session) so the request shares the browser's
|
|
104
115
|
// authenticated cookies. Disposed per call so no request context lingers and hangs the process.
|
|
105
116
|
const ctx: APIRequestContext = await request.newContext({
|
|
106
|
-
baseURL: base,
|
|
107
117
|
extraHTTPHeaders: headers,
|
|
108
118
|
timeout: conf.timeout_ms ?? 15000,
|
|
109
119
|
...(opts.storageState ? { storageState: opts.storageState } : {}),
|
|
110
120
|
});
|
|
111
121
|
try {
|
|
112
|
-
|
|
122
|
+
// Full URL (not a baseURL-relative path) so a base path component like /api/v3 is preserved.
|
|
123
|
+
const res = await ctx.fetch(joinApiUrl(base, urlPath), { method: req.method, ...bodyOpt });
|
|
113
124
|
const text = await res.text();
|
|
114
125
|
let parsed: any = text;
|
|
115
126
|
try { parsed = text ? JSON.parse(text) : null; } catch { /* non-JSON → keep text */ }
|