@sun-asterisk/sungen 3.2.16-beta.3 → 3.2.16-beta.5
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 +16 -5
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/exporters/matrix/build.d.ts +20 -2
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +78 -25
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +5 -4
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +15 -6
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/gates.d.ts.map +1 -1
- package/dist/exporters/matrix/gates.js +36 -1
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/map-loader.d.ts.map +1 -1
- package/dist/exporters/matrix/map-loader.js +2 -0
- package/dist/exporters/matrix/map-loader.js.map +1 -1
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +12 -9
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-xlsx.d.ts +14 -0
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +107 -48
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +21 -3
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/wording.d.ts +6 -0
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +22 -3
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +23 -6
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +31 -8
- package/package.json +3 -3
- package/src/cli/commands/delivery.ts +19 -6
- package/src/exporters/matrix/build.ts +77 -22
- package/src/exporters/matrix/export.ts +21 -6
- package/src/exporters/matrix/gates.ts +36 -1
- package/src/exporters/matrix/map-loader.ts +2 -0
- package/src/exporters/matrix/render-csv.ts +13 -10
- package/src/exporters/matrix/render-xlsx.ts +104 -49
- package/src/exporters/matrix/types.ts +22 -4
- package/src/exporters/matrix/wording.ts +20 -3
- package/src/orchestrator/templates/ai-src/commands/delivery.md +23 -6
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +31 -8
|
@@ -29,6 +29,10 @@ export interface MapGroup {
|
|
|
29
29
|
oracle: string;
|
|
30
30
|
category: MapCategory;
|
|
31
31
|
review: ReviewState;
|
|
32
|
+
/** Optional short digest of the coverage dimensions this item varies over
|
|
33
|
+
* (e.g. "required ×3 · format ×9 · full-width ×2"). Rendered on the parent row
|
|
34
|
+
* instead of concatenating every variant's title — the compact review view. */
|
|
35
|
+
dimensions?: string;
|
|
32
36
|
/** Variant refs: `VP-ID` (all @cases rows when the scenario has a dataset) or `VP-ID#label`. */
|
|
33
37
|
variants: string[];
|
|
34
38
|
}
|
|
@@ -57,7 +61,9 @@ export interface DeliveryMap {
|
|
|
57
61
|
/** Requirement-id → reviewed status override (partially_covered / not_applicable / …).
|
|
58
62
|
* Without an override a requirement is `covered` when a variant traces to it, else `gap`. */
|
|
59
63
|
requirements: Record<string, RequirementOverride>;
|
|
60
|
-
/** VP-id → scenario fingerprint stamped at approval time (drift detector input).
|
|
64
|
+
/** VP-id → scenario fingerprint stamped at approval time (drift detector input).
|
|
65
|
+
* The reserved key `__map__` holds the fingerprint of the map's own semantic
|
|
66
|
+
* content (groups + dispositions) so edited wording re-opens review (GAP-09). */
|
|
61
67
|
fingerprints: Record<string, string>;
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -116,7 +122,11 @@ export interface CoverageVariant {
|
|
|
116
122
|
result?: PlaywrightResult;
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
export type ItemResult = 'passed' | 'failed' | 'blocked' | 'partial' | 'not_run';
|
|
125
|
+
export type ItemResult = 'passed' | 'failed' | 'blocked' | 'partial' | 'not_run' | 'na';
|
|
126
|
+
|
|
127
|
+
/** The exact word rendered in a variant's Result cell — the single vocabulary the
|
|
128
|
+
* parent's roll-up formula counts (COUNTIF is exact-match). */
|
|
129
|
+
export type VariantState = 'Passed' | 'Failed' | 'Blocked' | 'Pending' | 'N/A';
|
|
120
130
|
|
|
121
131
|
/** One summarized row in the matrix — a group of variants sharing the signature. */
|
|
122
132
|
export interface DeliveryItem {
|
|
@@ -124,6 +134,8 @@ export interface DeliveryItem {
|
|
|
124
134
|
target: string;
|
|
125
135
|
intent: string;
|
|
126
136
|
oracle: string;
|
|
137
|
+
/** Short coverage-dimension digest from the map (compact parent view). */
|
|
138
|
+
dimensions?: string;
|
|
127
139
|
category: MapCategory;
|
|
128
140
|
review: ReviewState;
|
|
129
141
|
/** Highest priority among the variants (per-variant priorities stay on the sub-rows). */
|
|
@@ -134,13 +146,19 @@ export interface DeliveryItem {
|
|
|
134
146
|
layers: MatrixLayer[];
|
|
135
147
|
/** Union of variant traces (exact per-variant traces stay on the variants). */
|
|
136
148
|
traces: string[];
|
|
149
|
+
/** Preconditions shared by EVERY variant (the intersection) — written once here. */
|
|
137
150
|
precondition: string[];
|
|
138
|
-
/**
|
|
151
|
+
/** Leading trigger steps shared by every variant — written once here; each
|
|
152
|
+
* variant renders only its remaining steps (`triggerDeltas`). */
|
|
139
153
|
trigger: string[];
|
|
140
154
|
variants: CoverageVariant[];
|
|
155
|
+
/** variant ref → its precondition lines that are not on the parent. */
|
|
156
|
+
preconditionDeltas: Record<string, string[]>;
|
|
157
|
+
/** variant ref → its trigger steps after the shared prefix. */
|
|
158
|
+
triggerDeltas: Record<string, string[]>;
|
|
141
159
|
/** Derived roll-up — never entered independently (Gate F). */
|
|
142
160
|
result: ItemResult;
|
|
143
|
-
resultCounts: { passed: number; failed: number; blocked: number; notRun: number };
|
|
161
|
+
resultCounts: { passed: number; failed: number; blocked: number; notRun: number; na: number };
|
|
144
162
|
}
|
|
145
163
|
|
|
146
164
|
export interface MatrixDisposition {
|
|
@@ -19,10 +19,13 @@ function deRef(text: string): string {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function sentence(text: string): string {
|
|
22
|
-
|
|
22
|
+
// Trim the ENDS only — internal whitespace may be the test data itself
|
|
23
|
+
// (a padded email, a spaces-only value); collapsing it would silently
|
|
24
|
+
// change what the tester types (review GAP-04).
|
|
25
|
+
let s = text.trim();
|
|
23
26
|
if (!s) return s;
|
|
24
27
|
s = s.charAt(0).toUpperCase() + s.slice(1);
|
|
25
|
-
if (!/[.!?…]$/.test(s)) s += '.';
|
|
28
|
+
if (!/[.!?…"]$/.test(s)) s += '.';
|
|
26
29
|
return s;
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -42,7 +45,7 @@ export function renderAction(raw: string): string {
|
|
|
42
45
|
// click [X] <type>
|
|
43
46
|
[/^clicks? \[([^\]]+)\]\s*(\w+)?$/i, (m) => `Click the ${m[1]}${m[2] ? ` ${m[2]}` : ''}`],
|
|
44
47
|
// press <Key> at/in [X] field
|
|
45
|
-
[/^press(?:es)? (.+?) (?:at|in|inside) \[([^\]]+)\](?: field)?$/i, (m) => `Press ${m[1]} in the ${m[2]} field`],
|
|
48
|
+
[/^press(?:es)? (.+?) (?:at|in|on|inside) \[([^\]]+)\](?: field)?$/i, (m) => `Press ${m[1]} in the ${m[2]} field`],
|
|
46
49
|
[/^press(?:es)? (.+)$/i, (m) => `Press ${m[1]}`],
|
|
47
50
|
// select V in/from [X] dropdown
|
|
48
51
|
[/^selects? (.+?) (?:in|from) \[([^\]]+)\]\s*(\w+)?$/i, (m) => `Select ${m[1]} in the ${m[2]}${m[3] ? ` ${m[3]}` : ''}`],
|
|
@@ -103,9 +106,23 @@ export function renderPrecondition(raw: string): string {
|
|
|
103
106
|
const s = raw.trim().replace(/^(User|The user)\s+/i, '');
|
|
104
107
|
const m = s.match(/^is on \[([^\]]+)\] page(.*)$/i);
|
|
105
108
|
if (m) return sentence(`The user is on the ${m[1]} page${m[2] ?? ''}`);
|
|
109
|
+
// "wait for [X] <type> (is) visible" is a STATE once established, not an action.
|
|
110
|
+
const w = s.match(/^waits? for \[([^\]]+)\]\s*(\w+)?(?: is)?(?: visible)?$/i);
|
|
111
|
+
if (w) return sentence(`The ${w[1]}${w[2] ? ` ${w[2]}` : ''} is visible`);
|
|
112
|
+
const auth = s.match(/^is (signed|logged) (in|out)(.*)$/i);
|
|
113
|
+
if (auth) return sentence(`The user is ${auth[1]} ${auth[2]}${auth[3] ?? ''}`);
|
|
106
114
|
return sentence(deRef(`The user ${s.charAt(0).toLowerCase()}${s.slice(1)}`));
|
|
107
115
|
}
|
|
108
116
|
|
|
117
|
+
/**
|
|
118
|
+
* A manual `Setup:` line is an INSTRUCTION the tester performs to establish the
|
|
119
|
+
* state — imperative reads correctly ("Seed the locked account."), while
|
|
120
|
+
* "The user seed …" is broken grammar (review GAP-08).
|
|
121
|
+
*/
|
|
122
|
+
export function renderSetupInstruction(raw: string): string {
|
|
123
|
+
return renderAction(raw);
|
|
124
|
+
}
|
|
125
|
+
|
|
109
126
|
// ---------------------------------------------------------------------------
|
|
110
127
|
// Manual `# Tester verifies:` comment classification (structured, label-free)
|
|
111
128
|
// ---------------------------------------------------------------------------
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: delivery
|
|
3
3
|
description: "Export the Test Case & Coverage Matrix (review/manual/customer deliverable) from Gherkin + Playwright results. --legacy exports the classic per-scenario CSV/XLSX."
|
|
4
|
-
argument-hint: "[name...] [--env <locale>] [--legacy] (omit names for all
|
|
4
|
+
argument-hint: "[name...] [--format csv] [--env <locale>] [--legacy] (omit names for all)"
|
|
5
5
|
order: 50
|
|
6
6
|
claude-tools: "Bash, Read, Write, AskUserQuestion"
|
|
7
7
|
copilot-tools: "[read, execute, edit, vscode/askQuestions]"
|
|
@@ -27,6 +27,8 @@ review → approve → official render.
|
|
|
27
27
|
|
|
28
28
|
Parse from `$ARGUMENTS`:
|
|
29
29
|
- **names** — zero or more screen/flow/api names. Empty → all targets.
|
|
30
|
+
- **`--format <xlsx|csv|both>`** — output format. **Default `xlsx` — one artifact only.** Pass
|
|
31
|
+
`--format csv` when the user wants the flat CSV (pipelines/diffing), `--format both` for both.
|
|
30
32
|
- **`--env <locale>`** — sets `SUNGEN_ENV=<locale>` for the run (accept `--locale` as alias).
|
|
31
33
|
- **`--legacy` / `--full`** — pass through to the CLI and skip the map flow entirely.
|
|
32
34
|
|
|
@@ -37,8 +39,8 @@ Parse from `$ARGUMENTS`:
|
|
|
37
39
|
```bash
|
|
38
40
|
[ -x ./bin/sungen.js ] && ./bin/sungen.js delivery <names> || npx sungen delivery <names>
|
|
39
41
|
```
|
|
40
|
-
(prepend `SUNGEN_ENV=<locale>` when `--env` was given; append `--
|
|
41
|
-
to step 5.)
|
|
42
|
+
(prepend `SUNGEN_ENV=<locale>` when `--env` was given; append `--format <fmt>` when the user asked
|
|
43
|
+
for a non-default format; append `--legacy` when requested — then skip to step 5.)
|
|
42
44
|
|
|
43
45
|
Three outcomes per target:
|
|
44
46
|
- **Rendered** → done, go to step 5.
|
|
@@ -58,6 +60,7 @@ groups:
|
|
|
58
60
|
target: login.email # ONE target: field/component dot-path, flow phrase, or METHOD /path
|
|
59
61
|
intent: <one behavior/rule this item verifies>
|
|
60
62
|
oracle: <the shared observable Pass/Fail statement>
|
|
63
|
+
dimensions: violated rule — required ×3 · format ×10 · full-width ×2 # see below
|
|
61
64
|
category: normal | abnormal | security | nfr
|
|
62
65
|
review: proposed # ALWAYS proposed — only QA approval flips it
|
|
63
66
|
variants: [VP-VAL-001-B, VP-VAL-001-S] # VP-ids; bare id on a @cases scenario = all its rows
|
|
@@ -92,6 +95,17 @@ determined** (its oracle *family*, not its exact message).
|
|
|
92
95
|
disposition). Data-setup blocks (`@manual:data-setup`) → `excluded`; SPEC-GAP placeholders →
|
|
93
96
|
`blocked`.
|
|
94
97
|
|
|
98
|
+
**`dimensions:` — the compact coverage digest (required for items with >3 variants).**
|
|
99
|
+
This one short line is what the collapsed parent row shows instead of listing every variant, so a
|
|
100
|
+
reviewer sees *which dimensions* the item covers without expanding it. Name the dimension, then the
|
|
101
|
+
branches with counts:
|
|
102
|
+
- `violated rule — required ×3 · format ×10 · full-width ×2`
|
|
103
|
+
- `account state — wrong password · unregistered · locked · soft-deleted`
|
|
104
|
+
- `submission method — Login button · Enter in Password · Enter in Email`
|
|
105
|
+
|
|
106
|
+
Keep it ≤120 chars (Gate W warns). **YAML caveat:** a bare `: ` inside the value breaks the parse —
|
|
107
|
+
use ` — ` as the label separator (as above) or quote the whole string.
|
|
108
|
+
|
|
95
109
|
**Wording rules for `intent`/`oracle` (customer-facing — Gate W lints these):**
|
|
96
110
|
- Plain product language, present simple, ~10–20 words, one behavior:
|
|
97
111
|
"A user can sign in with valid credentials and is redirected to the Jobs page."
|
|
@@ -151,6 +165,7 @@ counted in variants, never items**). Then `AskUserQuestion`:
|
|
|
151
165
|
- **Open the workbook** — inspect `qa/deliverables/<unit>-testcases.xlsx` (Testcases sheet:
|
|
152
166
|
collapse outline level 1 for the customer view; Coverage sheet: target × category grid + gaps).
|
|
153
167
|
- **Run tests to refresh results** — `/sungen:run-test <unit>`, then re-run delivery.
|
|
168
|
+
- **Also export CSV** — `sungen delivery <unit> --format csv` (flat `item`/`variant` rows for pipelines).
|
|
154
169
|
- **Export the legacy workbook too** — `sungen delivery <unit> --legacy`.
|
|
155
170
|
- **Done**
|
|
156
171
|
|
|
@@ -168,7 +183,8 @@ counted in variants, never items**). Then `AskUserQuestion`:
|
|
|
168
183
|
## CLI reference
|
|
169
184
|
|
|
170
185
|
```
|
|
171
|
-
sungen delivery [names...] # matrix (default; needs the map)
|
|
186
|
+
sungen delivery [names...] # matrix (default; needs the map) → XLSX only
|
|
187
|
+
--format <xlsx|csv|both> # output format; default xlsx (one artifact)
|
|
172
188
|
--check # gates only — validate the map, write nothing
|
|
173
189
|
--approve [DI-a,DI-b] # flip proposed→approved (+ stamp fingerprints); all groups when bare
|
|
174
190
|
--preview # render despite review findings (DRAFT watermark)
|
|
@@ -176,5 +192,6 @@ sungen delivery [names...] # matrix (default; needs the map)
|
|
|
176
192
|
--skip-preflight | --continue-on-missing | --env <env> # as before
|
|
177
193
|
```
|
|
178
194
|
|
|
179
|
-
Outputs: `qa/deliverables/<unit>-testcases.xlsx` (Testcases + Coverage sheets)
|
|
180
|
-
(flat, `Level` column `item|variant`)
|
|
195
|
+
Outputs: `qa/deliverables/<unit>-testcases.xlsx` (Testcases + Coverage sheets) by default;
|
|
196
|
+
`--format csv` writes `<unit>-testcases.csv` instead (flat, `Level` column `item|variant`),
|
|
197
|
+
`--format both` writes both. Legacy mode writes the classic files instead.
|
|
@@ -36,7 +36,8 @@ oracle-shape/precondition mismatches are review-level, silenced once approved an
|
|
|
36
36
|
D executability (precondition · condition+
|
|
37
37
|
data · trigger · oracle all renderable; every `{{var}}` resolves; **no template token may survive
|
|
38
38
|
into a rendered cell** — test-data cross-references are resolved for display) · E drift
|
|
39
|
-
(fingerprint mismatch → back to review
|
|
39
|
+
(scenario fingerprint mismatch → back to review; the map's OWN reviewed
|
|
40
|
+
wording/grouping is fingerprinted as `__map__` too, so post-approval edits re-open review) · G review state (proposed groups block the official
|
|
40
41
|
render; `--preview` renders a DRAFT watermark) · R requirement coverage (spec FR/TR/NFR ids with
|
|
41
42
|
no trace and no `requirements:` status → warning) · W wording lint (map intent/oracle containing
|
|
42
43
|
tokens, `[Selector]` refs, DSL phrasing, or generator labels → warning).
|
|
@@ -51,17 +52,39 @@ Precondition, `Action:` → Action, `Observable:` → Expected Result, `Oracle:`
|
|
|
51
52
|
mid-flow assertions inline as `Verify: …`; only the final Then block is the Expected Result.
|
|
52
53
|
Empty test values render as `(empty)`.
|
|
53
54
|
|
|
55
|
+
**Output format**: **XLSX only by default** (one artifact). `--format csv` writes the flat CSV
|
|
56
|
+
instead; `--format both` writes both.
|
|
57
|
+
|
|
54
58
|
**Workbook**: `Testcases` sheet — parent rows + outline-level-1 variant sub-rows for **every**
|
|
55
59
|
item (single-variant included: the sub-row carries the source VP-id, resolved data, and the
|
|
56
|
-
result/evidence entry).
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
result/evidence entry). Sub-rows are **delta-only**: knowledge common to the whole item
|
|
61
|
+
(precondition, trigger) is written ONCE on the parent; a child repeats only what distinguishes it
|
|
62
|
+
(condition/data, precondition delta, trigger when it differs) plus its own precise oracle and
|
|
63
|
+
execution fields. The parent never carries placeholder text — when triggers differ the cell is
|
|
64
|
+
simply empty and the sub-rows carry them.
|
|
65
|
+
|
|
66
|
+
Parent preconditions are the **intersection** of the variants' and the parent's Action holds the
|
|
67
|
+
**shared leading steps**; each variant renders only its remaining steps, numbered to continue the
|
|
68
|
+
parent's list — so a shared prefix is written once, not repeated on every child. The parent's
|
|
69
|
+
Coverage cell is the map's short `dimensions:` digest (or the variant refs when none is declared),
|
|
70
|
+
never a concatenation of variant titles.
|
|
71
|
+
|
|
72
|
+
Variant Result cells hold exactly one of `Passed`/`Failed`/`Blocked`/`Pending`/`N/A` (dropdown) —
|
|
73
|
+
that vocabulary is the contract: the parent Result and the summary band are **live Excel formulas**
|
|
74
|
+
counting those words, so a parent label is always a composed `n/m Passed …` string (`All N/A` when
|
|
75
|
+
every variant is N/A) and never a bare state word. **`N/A` leaves the denominator.** Evidence and
|
|
76
|
+
Defect ID are separate columns; ID + Target frozen; ISO dates; landscape print with repeated
|
|
77
|
+
header rows.
|
|
78
|
+
|
|
79
|
+
`Coverage` sheet — requirement coverage table (every FR/TR/NFR id with an explicit status),
|
|
80
|
+
target × category grid with explicit `—` gaps, dispositions, manifest. CSV mirrors the same model
|
|
81
|
+
flat with a `Level` column (`item`/`variant`) + a requirement-coverage appendix.
|
|
63
82
|
`delivery_item_count` ≠ progress — variants are the execution metric.
|
|
64
83
|
|
|
84
|
+
**Data fidelity**: invisible test data is made visible, never normalized — `''` → `(empty)`,
|
|
85
|
+
whitespace-only → `(5 spaces)`, padded → `" value "` quoted verbatim. A trim/collapse here would
|
|
86
|
+
silently break the whitespace tests it describes.
|
|
87
|
+
|
|
65
88
|
**Authoring guidance the matrix rewards** (create-test side): payload/provider matrices (SQLi
|
|
66
89
|
payload lists, OAuth provider sets) belong in `@cases` datasets so each case is an atomic,
|
|
67
90
|
independently-reportable variant; keep dataset `case:` labels short and stable (`CHK-EMAIL-I1`),
|