@sun-asterisk/sungen 3.0.0-beta.74 → 3.0.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.
Files changed (41) hide show
  1. package/dist/cli/commands/audit.d.ts.map +1 -1
  2. package/dist/cli/commands/audit.js +17 -3
  3. package/dist/cli/commands/audit.js.map +1 -1
  4. package/dist/generators/gherkin-parser/index.d.ts +2 -0
  5. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  6. package/dist/generators/gherkin-parser/index.js +15 -0
  7. package/dist/generators/gherkin-parser/index.js.map +1 -1
  8. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +8 -0
  9. package/dist/generators/test-generator/patterns/capture-patterns.d.ts +5 -0
  10. package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +1 -1
  11. package/dist/generators/test-generator/patterns/capture-patterns.js +33 -0
  12. package/dist/generators/test-generator/patterns/capture-patterns.js.map +1 -1
  13. package/dist/harness/audit.d.ts +5 -1
  14. package/dist/harness/audit.d.ts.map +1 -1
  15. package/dist/harness/audit.js +13 -2
  16. package/dist/harness/audit.js.map +1 -1
  17. package/dist/harness/parse.d.ts +1 -0
  18. package/dist/harness/parse.d.ts.map +1 -1
  19. package/dist/harness/parse.js +3 -0
  20. package/dist/harness/parse.js.map +1 -1
  21. package/dist/harness/provenance.d.ts +6 -0
  22. package/dist/harness/provenance.d.ts.map +1 -0
  23. package/dist/harness/provenance.js +65 -0
  24. package/dist/harness/provenance.js.map +1 -0
  25. package/dist/harness/sensors.d.ts +30 -0
  26. package/dist/harness/sensors.d.ts.map +1 -1
  27. package/dist/harness/sensors.js +122 -0
  28. package/dist/harness/sensors.js.map +1 -1
  29. package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +12 -0
  30. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +12 -0
  31. package/package.json +3 -3
  32. package/src/cli/commands/audit.ts +13 -3
  33. package/src/generators/gherkin-parser/index.ts +17 -0
  34. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +8 -0
  35. package/src/generators/test-generator/patterns/capture-patterns.ts +38 -0
  36. package/src/harness/audit.ts +18 -4
  37. package/src/harness/parse.ts +4 -0
  38. package/src/harness/provenance.ts +33 -0
  39. package/src/harness/sensors.ts +189 -0
  40. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +12 -0
  41. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +12 -0
@@ -40,6 +40,8 @@ exports.dataThemesFor = dataThemesFor;
40
40
  exports.coverageBalance = coverageBalance;
41
41
  exports.duplicateClusters = duplicateClusters;
42
42
  exports.traceability = traceability;
43
+ exports.taxonomyLint = taxonomyLint;
44
+ exports.claimProof = claimProof;
43
45
  /**
44
46
  * Harness Sensors — deterministic quality measurement over test-design artifacts.
45
47
  * Each sensor returns a structured finding the Orchestrator/Repair loop can act on.
@@ -229,4 +231,124 @@ function traceability(scenarios, viewpoints) {
229
231
  : 'Traceable.',
230
232
  };
231
233
  }
234
+ // Ordered by specificity; the first matching rule per scenario wins.
235
+ const CLAIM_RULES = [
236
+ {
237
+ claim: 'all/every/each',
238
+ title: /\b(all|every|each)\b/,
239
+ proof: /\bsee all\b/,
240
+ need: 'a `User see all …` assertion (every element, not one)',
241
+ hint: 'assert across all matched elements — for per-card structure use the all-card DSL (roadmap Q2).',
242
+ severity: 'fail',
243
+ // singular action presence under an "each/every" claim = partial proof
244
+ underproof: /\bsee \[[^\]]+\] (link|button)\b/,
245
+ underNeed: 'an all-card assertion for the claimed action (not single presence)',
246
+ underHint: 'the actions are asserted once, not per-card — wrap them with the all-card DSL `User see all [Card] contain [Action]` (roadmap Q2).',
247
+ },
248
+ {
249
+ claim: 'only/belong-to',
250
+ title: /\b(only|belongs? to|exclusively)\b/,
251
+ proof: /\bsee all\b[^.]*\bcontain/,
252
+ need: 'all-match (`User see all … contains …`)',
253
+ hint: 'all displayed items must match the facet; "no items OUTSIDE" is UI-limited → verify via hybrid API/DB or mark @manual with reason.',
254
+ severity: 'warn',
255
+ },
256
+ {
257
+ claim: 'single/exactly-one',
258
+ title: /\b(a single|single|exactly one|just one|only one)\b/,
259
+ proof: /\bcount\b|\btable with\b|\bexactly\b|tohavecount/,
260
+ need: 'a count assertion (e.g. count is 1)',
261
+ hint: 'assert the quantity, not just presence.',
262
+ severity: 'fail',
263
+ },
264
+ {
265
+ claim: 'correct/matching/identity',
266
+ title: /\b(correct|matching|the same|same (product|name|price|value|item)|consistency|identity|lands? in|is the (product|item))\b/,
267
+ proof: /\bremember\b/,
268
+ need: 'capture + compare (`User remember … as {{v}}` then assert {{v}})',
269
+ hint: 'remember the value on the source, then assert it on the target (cross-screen → defer to a flow as @manual).',
270
+ severity: 'fail',
271
+ },
272
+ {
273
+ claim: 'changes/toggles',
274
+ title: /\b(changes?|toggles?|updates?|reflects?|reopens?)\b/,
275
+ proof: /\bremember\b|\bis hidden\b/,
276
+ need: 'a state delta (before ≠ after, or a visible→hidden contrast)',
277
+ hint: 'capture the before-state and assert the after-state differs, or assert the visible/hidden transition.',
278
+ severity: 'warn',
279
+ },
280
+ {
281
+ claim: 'hidden/rejected/not-complete',
282
+ title: /\b(hidden|closed|dismiss(es|ed)?|does not|doesn't|not complete|rejected|inert)\b/,
283
+ proof: /\bis hidden\b|\bare hidden\b|message is hidden|not complete|\bhidden\b/,
284
+ need: 'a negative / hidden assertion (`… is hidden`)',
285
+ hint: 'assert the absence/hidden state that the title claims, not just an unrelated visible element.',
286
+ severity: 'fail',
287
+ },
288
+ ];
289
+ const TAXONOMY_KNOWN = new Set(['LIST', 'PRODUCT', 'CART', 'FILTER', 'NAV', 'VAL', 'SEC']);
290
+ const CATEGORY_SIGNALS = [
291
+ { cat: 'FILTER', re: /\b(categor(y|ies)|sub-?categor\w*|expand\w*|collaps\w*|\bfacet\b|filter\w*|belongs? to)\b/ },
292
+ { cat: 'CART', re: /\b(add(ed|ing)? to cart|added (dialog|modal|message|confirmation)|cart (page|table|line|item)|view cart|continue shopping|quantit)\b/ },
293
+ { cat: 'PRODUCT', re: /\b(product detail|product-detail|view product|product page|same product|product name and price|product identity|detail product)\b/ },
294
+ { cat: 'LIST', re: /\b(product (list|grid)|featured product|product card|each (featured )?card|every (featured )?card)\b/ },
295
+ { cat: 'NAV', re: /\b(navigates?|returns the user|redirect\w*|logo returns|menu navigates)\b/ },
296
+ { cat: 'VAL', re: /\b(valid email|invalid\b|without (a |an )?@|without a domain|empty (email|input|field)|validation|required field)\b/ },
297
+ { cat: 'SEC', re: /\b(xss|sql|injection|payload|without authentication|unauthenticated|\binert\b|tamper\w*|malformed|url parameter|query param\w*|falls? back|gracefull?y|without a crash|not[- ]found|\b404\b|special character|abuse)\b/ },
298
+ ];
299
+ function taxonomyLint(scenarios) {
300
+ const mislabeled = [];
301
+ let checked = 0;
302
+ for (const s of scenarios) {
303
+ const cat = s.category;
304
+ if (!cat || !TAXONOMY_KNOWN.has(cat))
305
+ continue;
306
+ checked++;
307
+ const title = s.name.toLowerCase();
308
+ const matched = [...new Set(CATEGORY_SIGNALS.filter((c) => c.re.test(title)).map((c) => c.cat))];
309
+ if (matched.includes(cat))
310
+ continue; // title supports its own category → fine
311
+ const others = matched.filter((c) => c !== cat);
312
+ if (others.length === 1) { // unambiguous mismatch
313
+ const sig = CATEGORY_SIGNALS.find((c) => c.cat === others[0]);
314
+ const m = title.match(sig.re);
315
+ mislabeled.push({ name: s.name, current: cat, suggested: others[0], signal: m ? m[0] : others[0] });
316
+ }
317
+ }
318
+ return { total: scenarios.length, checked, mislabeled };
319
+ }
320
+ function claimProof(scenarios, focus = 'functional') {
321
+ const measured = scenarios.filter((s) => !s.manual);
322
+ const unproven = [];
323
+ let withClaims = 0;
324
+ for (const s of measured) {
325
+ const title = s.name.toLowerCase();
326
+ const rule = CLAIM_RULES.find((r) => r.title.test(title));
327
+ if (!rule)
328
+ continue; // no claim → not measured
329
+ withClaims++;
330
+ if (!rule.proof.test(s.stepsText)) {
331
+ // proof shape entirely absent → fail at the rule's severity
332
+ unproven.push({ name: s.name, claim: rule.claim, need: rule.need, hint: rule.hint, severity: rule.severity });
333
+ }
334
+ else if (rule.underproof && rule.underproof.test(s.stepsText)) {
335
+ // right shape exists but only as a partial proof → warn
336
+ unproven.push({
337
+ name: s.name, claim: rule.claim,
338
+ need: rule.underNeed ?? rule.need,
339
+ hint: rule.underHint ?? rule.hint,
340
+ severity: 'warn',
341
+ });
342
+ }
343
+ }
344
+ const proven = withClaims - unproven.length;
345
+ const ratio = withClaims ? proven / withClaims : 1;
346
+ const threshold = DEPTH_THRESHOLDS[focus] ?? DEPTH_THRESHOLDS.functional;
347
+ const hasFail = unproven.some((u) => u.severity === 'fail');
348
+ let verdict = 'pass';
349
+ if (ratio < threshold) {
350
+ verdict = WARN_ONLY_FOCUS.has(focus) || !hasFail ? 'warn' : 'fail';
351
+ }
352
+ return { total: measured.length, withClaims, proven, unproven, ratio, focus, threshold, verdict };
353
+ }
232
354
  //# sourceMappingURL=sensors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sensors.js","sourceRoot":"","sources":["../../src/harness/sensors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sensors.js","sourceRoot":"","sources":["../../src/harness/sensors.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,kCAGC;AAkBD,sCAyCC;AAkCD,wCAwCC;AAGD,sCAKC;AAaD,0CA0BC;AAUD,8CAoBC;AAaD,oCAeC;AAsID,oCAkBC;AAED,gCAmCC;AAndD;;;;;;;GAOG;AACH,uCAAyB;AACzB,2CAA6B;AAC7B,+BAA0C;AAG1C,oFAAoF;AACpF,MAAM,sBAAsB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAE1F,gCAAgC;AAChC,MAAM,OAAO,GAA6B;IACxC,eAAe,EAAE,sBAAsB;IACvC,cAAc,EAAE,CAAC,IAAI,CAAC;IACtB,qBAAqB,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC;IAC5C,UAAU,EAAE,CAAC,OAAO,CAAC;IACrB,YAAY,EAAE,CAAC,KAAK,CAAC;CACtB,CAAC;AAcF,SAAgB,WAAW;IACzB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,2BAA2B,CAAC,CAAC;IACvE,OAAO,IAAA,YAAS,EAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAY,CAAC;AAC3D,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,SAAmB,EAAE,EAAU,EAAE,EAAE;IAC9C,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3B,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC,CAAC;AAaF,SAAgB,aAAa,CAAC,SAAyB,EAAE,UAA4B,EAAE,OAAgB;IACrG,MAAM,SAAS,GAAG;QAChB,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;QACnC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;KAC9D,CAAC;IAEF,gDAAgD;IAChD,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QACzE,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;YAAC,IAAI,GAAG,IAAI,CAAC;YAAC,QAAQ,GAAG,EAAE,CAAC;QAAC,CAAC;IAClD,CAAC;IAED,MAAM,IAAI,GAAuB,EAAE,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC;IAC3B,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,UAAU,EAAE,CAAC;YACxD,KAAK,EAAE,CAAC;YACR,yEAAyE;YACzE,yEAAyE;YACzE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;YACvG,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;YACtD,IAAI,IAAI;gBAAE,OAAO,EAAE,CAAC;iBACf,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;;gBAChG,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS;SACpC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;SAChF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAEvB,OAAO;QACL,QAAQ;QACR,WAAW,EAAE,KAAK;QAClB,aAAa,EAAE,OAAO;QACtB,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI;QACJ,aAAa;KACd,CAAC;AACJ,CAAC;AAoBD,kFAAkF;AAClF,4EAA4E;AAC5E,MAAM,gBAAgB,GAA2B;IAC/C,UAAU,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG;CAC9D,CAAC;AACF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAE3C;;;;;;GAMG;AACH,SAAgB,cAAc,CAC5B,SAAyB,EACzB,aAA6B,EAAE,EAC/B,KAAK,GAAG,YAAY;IAEpB,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAEnD,gGAAgG;IAChG,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7G,uFAAuF;IACvF,qFAAqF;IACrF,yFAAyF;IACzF,MAAM,iBAAiB,GAAG,CAAC,CAAe,EAAE,EAAE,CAC5C,4JAA4J,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAChL,MAAM,eAAe,GAAG,CAAC,CAAe,EAAE,EAAE,CAC1C,CAAC,CAAC,QAAQ,KAAK,KAAK,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAEnH,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACrD,+EAA+E;IAC/E,iFAAiF;IACjF,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAE5E,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC;IACzE,IAAI,OAAO,GAAiB,MAAM,CAAC;IACnC,IAAI,KAAK,GAAG,SAAS;QAAE,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAE9E,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,MAAM;QACvB,YAAY,EAAE,OAAO,CAAC,MAAM;QAC5B,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACtE,qBAAqB,EAAE,QAAQ,CAAC,MAAM;QACtC,uBAAuB,EAAE,UAAU,CAAC,MAAM;QAC1C,YAAY,EAAE,KAAK;QACnB,uBAAuB,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;QACxF,KAAK;QACL,SAAS;QACT,OAAO;KACR,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,SAAgB,aAAa,CAAC,OAAgB,EAAE,QAAuB;IACrE,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,IAAI,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,CAAC;IACtG,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAK,gBAAgB,CAAC,CAAC;AACtE,CAAC;AAaD,SAAgB,eAAe,CAAC,SAAyB;IACvD,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACtD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC;QACjC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;QAC9F,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,QAAQ,CAAC,qBAAqB,CAAC,CAAC;IAC7E,MAAM,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,CAAC,CAAC;IACtD,OAAO;QACL,QAAQ;QACR,UAAU;QACV,SAAS,EAAE,IAAI;QACf,cAAc,EAAE,SAAS;QACzB,UAAU;QACV,IAAI,EAAE,UAAU;YACd,CAAC,CAAC,4DAA4D,SAAS,6BAA6B,IAAI,aAAa;YACrH,CAAC,CAAC,WAAW;KAChB,CAAC;AACJ,CAAC;AAUD,SAAgB,iBAAiB,CAAC,SAAyB;IACzD,MAAM,GAAG,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QAC1C,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACZ,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;IAC/B,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;SACnC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QACzB,QAAQ,EAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ;QAC3E,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjC,iGAAiG;QACjG,cAAc,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;KACzC,CAAC,CAAC,CAAC;IACN,OAAO;QACL,QAAQ;QACR,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/G,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;KACrE,CAAC;AACJ,CAAC;AAaD,SAAgB,YAAY,CAAC,SAAyB,EAAE,UAA4B;IAClF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnD,iGAAiG;IACjG,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,MAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;IACrI,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,MAAM;QACvB,UAAU,EAAE,QAAQ,CAAC,MAAM;QAC3B,gBAAgB,EAAE,MAAM,CAAC,MAAM;QAC/B,eAAe,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1E,WAAW,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,GAAG;YACzC,CAAC,CAAC,uHAAuH;YACzH,CAAC,CAAC,YAAY;KACjB,CAAC;AACJ,CAAC;AA6CD,qEAAqE;AACrE,MAAM,WAAW,GAAgB;IAC/B;QACE,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,sBAAsB;QAC7B,KAAK,EAAE,aAAa;QACpB,IAAI,EAAE,uDAAuD;QAC7D,IAAI,EAAE,gGAAgG;QACtG,QAAQ,EAAE,MAAM;QAChB,uEAAuE;QACvE,UAAU,EAAE,kCAAkC;QAC9C,SAAS,EAAE,oEAAoE;QAC/E,SAAS,EAAE,oIAAoI;KAChJ;IACD;QACE,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,oCAAoC;QAC3C,KAAK,EAAE,2BAA2B;QAClC,IAAI,EAAE,yCAAyC;QAC/C,IAAI,EAAE,oIAAoI;QAC1I,QAAQ,EAAE,MAAM;KACjB;IACD;QACE,KAAK,EAAE,oBAAoB;QAC3B,KAAK,EAAE,qDAAqD;QAC5D,KAAK,EAAE,kDAAkD;QACzD,IAAI,EAAE,qCAAqC;QAC3C,IAAI,EAAE,yCAAyC;QAC/C,QAAQ,EAAE,MAAM;KACjB;IACD;QACE,KAAK,EAAE,2BAA2B;QAClC,KAAK,EAAE,2HAA2H;QAClI,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,kEAAkE;QACxE,IAAI,EAAE,6GAA6G;QACnH,QAAQ,EAAE,MAAM;KACjB;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,KAAK,EAAE,qDAAqD;QAC5D,KAAK,EAAE,4BAA4B;QACnC,IAAI,EAAE,8DAA8D;QACpE,IAAI,EAAE,uGAAuG;QAC7G,QAAQ,EAAE,MAAM;KACjB;IACD;QACE,KAAK,EAAE,8BAA8B;QACrC,KAAK,EAAE,kFAAkF;QACzF,KAAK,EAAE,wEAAwE;QAC/E,IAAI,EAAE,+CAA+C;QACrD,IAAI,EAAE,+FAA+F;QACrG,QAAQ,EAAE,MAAM;KACjB;CACF,CAAC;AAuBF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAE3F,MAAM,gBAAgB,GAAkC;IACtD,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,2FAA2F,EAAE;IAClH,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,sIAAsI,EAAE;IAC3J,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,mIAAmI,EAAE;IAC3J,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,sGAAsG,EAAE;IAC3H,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,2EAA2E,EAAE;IAC/F,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,qHAAqH,EAAE;IACzI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,wNAAwN,EAAE;CAC7O,CAAC;AAEF,SAAgB,YAAY,CAAC,SAAyB;IACpD,MAAM,UAAU,GAAsB,EAAE,CAAC;IACzC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvB,IAAI,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC/C,OAAO,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACjG,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS,CAAa,yCAAyC;QAC1F,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;QAChD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC,CAAwB,uBAAuB;YACvE,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAE,CAAC;YAC/D,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC9B,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtG,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AAC1D,CAAC;AAED,SAAgB,UAAU,CAAC,SAAyB,EAAE,KAAK,GAAG,YAAY;IACxE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI;YAAE,SAAS,CAAU,0BAA0B;QACxD,UAAU,EAAE,CAAC;QACb,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,4DAA4D;YAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChH,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,wDAAwD;YACxD,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK;gBAC/B,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI;gBACjC,IAAI,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI;gBACjC,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,SAAS,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAEzE,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC;IAC5D,IAAI,OAAO,GAAiB,MAAM,CAAC;IACnC,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;QACtB,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IACrE,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACpG,CAAC"}
@@ -74,6 +74,18 @@ User switch to [T] frame | [main] frame
74
74
  # 8. Page: User see [T] page
75
75
  ```
76
76
 
77
+ ### Collection / all-card (P5)
78
+
79
+ ```
80
+ # Every element's TEXT matches a value (filter correctness, all items belong):
81
+ User see all [Result Product Name] text contains {{category_term}}
82
+ # Every CONTAINER holds a CHILD element (structural per-card proof — prove "each card has X"):
83
+ User see all [Product Card] contain [Product Name]
84
+ User see all [Product Card] contain [Add To Cart] button
85
+ ```
86
+
87
+ Use the all-card form whenever a title claims *every / each* card/row exposes something — a single `User see [Add To Cart] button` does NOT prove "each card" and the harness Claim-Proof gate will flag it.
88
+
77
89
  ### Table
78
90
 
79
91
  ```
@@ -74,6 +74,18 @@ User switch to [T] frame | [main] frame
74
74
  # 8. Page: User see [T] page
75
75
  ```
76
76
 
77
+ ### Collection / all-card (P5)
78
+
79
+ ```
80
+ # Every element's TEXT matches a value (filter correctness, all items belong):
81
+ User see all [Result Product Name] text contains {{category_term}}
82
+ # Every CONTAINER holds a CHILD element (structural per-card proof — prove "each card has X"):
83
+ User see all [Product Card] contain [Product Name]
84
+ User see all [Product Card] contain [Add To Cart] button
85
+ ```
86
+
87
+ Use the all-card form whenever a title claims *every / each* card/row exposes something — a single `User see [Add To Cart] button` does NOT prove "each card" and the harness Claim-Proof gate will flag it.
88
+
77
89
  ### Table
78
90
 
79
91
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "3.0.0-beta.74",
3
+ "version": "3.0.0",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -12,8 +12,8 @@
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/ && 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",
16
- "test:update": "tsx tests/golden/run.ts --update",
15
+ "test": "tsx tests/golden/run.ts && tsx tests/audit/run.ts",
16
+ "test:update": "tsx tests/golden/run.ts --update && tsx tests/audit/run.ts --update",
17
17
  "prepublishOnly": "npm run build:dashboard && npm run build"
18
18
  },
19
19
  "keywords": [
@@ -23,8 +23,10 @@ function render(r: AuditReport): void {
23
23
  L('');
24
24
  L(` Quality score (business-weighted): ${r.score.overall}/10 [Viewpoint Gate: ${r.gateStatus}]`);
25
25
  L(` Intent: focus=${r.intent.focus} · depth threshold ${r.depth.threshold.toFixed(2)} (${r.intent.source})`);
26
+ L(` Provenance: sungen ${r.provenance.sungenVersion} · catalog ${r.provenance.catalogHash} (same here on every machine → same score)`);
26
27
  L(` coverage ${bar(r.score.coverage)} ${(r.score.coverage * 100).toFixed(0)}%`);
27
28
  L(` businessDepth ${bar(r.score.businessDepth)} ${(r.score.businessDepth * 100).toFixed(0)}% [${r.depth.verdict.toUpperCase()}]`);
29
+ L(` claimProof ${bar(r.claim.ratio)} ${(r.claim.ratio * 100).toFixed(0)}% [${r.claim.verdict.toUpperCase()}]`);
28
30
  L(` balance ${bar(r.score.balance)} ${(r.score.balance * 100).toFixed(0)}%`);
29
31
  L(` traceability ${bar(r.score.traceability)} ${(r.score.traceability * 100).toFixed(0)}%`);
30
32
  L(` (${r.score.formula})`);
@@ -37,15 +39,23 @@ function render(r: AuditReport): void {
37
39
  for (const s of r.depth.shallowBusinessCritical.slice(0, 8)) L(` ⚠ ${s.category}: ${s.name}`);
38
40
  if (r.depth.shallowBusinessCritical.length > 8) L(` … +${r.depth.shallowBusinessCritical.length - 8} more`);
39
41
  L('');
40
- L(` ③ Coverage balance — ${r.balance.imbalanced ? '⚠ IMBALANCED' : '✓ balanced'}`);
42
+ L(` ③ Claim-Proof — ${r.claim.proven}/${r.claim.withClaims} titled claims proven by steps [${r.claim.verdict.toUpperCase()}]`);
43
+ if (r.claim.unproven.length === 0) L(' ✓ every claim (all/only/single/correct/changes/hidden) is backed by an assertion');
44
+ for (const u of r.claim.unproven.slice(0, 8)) L(` ${u.severity === 'fail' ? '✗' : '⚠'} [${u.claim}] ${u.name} → needs ${u.need}`);
45
+ if (r.claim.unproven.length > 8) L(` … +${r.claim.unproven.length - 8} more`);
46
+ L('');
47
+ L(` ③·5 Taxonomy — ${r.taxonomy.mislabeled.length === 0 ? '✓ VP codes match scenario semantics' : `⚠ ${r.taxonomy.mislabeled.length} mislabel(s)`}`);
48
+ for (const m of r.taxonomy.mislabeled.slice(0, 6)) L(` ⚠ ${m.name} → VP-${m.current} reads as ${m.suggested} ("${m.signal}")`);
49
+ L('');
50
+ L(` ④ Coverage balance — ${r.balance.imbalanced ? '⚠ IMBALANCED' : '✓ balanced'}`);
41
51
  L(` buckets: ${Object.entries(r.balance.byBucket).map(([k, v]) => `${k}=${v}`).join(' ')}`);
42
52
  L('');
43
- L(` Duplicate — ${r.duplicates.clusters.length} same-shape cluster(s), ${r.duplicates.exactDuplicateCount} likely exact dup(s)`);
53
+ L(` Duplicate — ${r.duplicates.clusters.length} same-shape cluster(s), ${r.duplicates.exactDuplicateCount} likely exact dup(s)`);
44
54
  for (const c of r.duplicates.clusters.slice(0, 3)) {
45
55
  L(` ${c.sameDataLikely ? '✗ exact' : '○ EP/data family'} (${c.scenarios.length}): ${c.scenarios.slice(0, 3).join(' | ')}${c.scenarios.length > 3 ? ' …' : ''}`);
46
56
  }
47
57
  L('');
48
- L(` Traceability — ${(r.trace.mappedRatio * 100).toFixed(0)}% scenarios linked to viewpoint-overview`);
58
+ L(` Traceability — ${(r.trace.mappedRatio * 100).toFixed(0)}% scenarios linked to viewpoint-overview`);
49
59
  L(` ${r.trace.note}`);
50
60
  L('');
51
61
  L(' ── Findings (Repair targets) ──');
@@ -18,6 +18,8 @@ export interface ParsedStep {
18
18
  keyword: string; // Given, When, Then, And, But
19
19
  text: string; // Original step text
20
20
  selectorRef?: string; // Extracted selector reference like [auth-login.email]
21
+ childRef?: string; // Q2: second [ref] after "contain" — child element for all-card structural assertion
22
+ childElementType?: string; // Element type after the child [ref], e.g. "button" in "contain [Add To Cart] button"
21
23
  dataRef?: string; // Extracted data reference like <valid_user.email>
22
24
  value?: string; // Static value if any
23
25
  elementType?: string; // Element type after [Selector] e.g., "button", "text", "link", "field"
@@ -173,12 +175,25 @@ export class GherkinParser {
173
175
  if (selectorRef) {
174
176
  const elementTypeMatch = text.match(/\]\s+([\w-]+)/);
175
177
  elementType = elementTypeMatch ? elementTypeMatch[1].toLowerCase() : undefined;
178
+ // In "see all [Card] contain [Child]" the word after the first `]` is the
179
+ // connector "contain", not an element type — don't mistake it for one (Q2).
180
+ if (elementType && /^(contain|contains|include|includes)$/.test(elementType)) elementType = undefined;
176
181
  } else {
177
182
  // Extract final word if it's a known type (page, button, field, etc)
178
183
  const finalWordMatch = text.match(/\b(page|button|field|link|text|checkbox|radio|select|dropdown|modal|dialog|drawer|message|error|warning|success|alert|table|list|list-item|listitem|row|search|option|slider|toggle|tab|breadcrumb|overlay|step|section|card|item|icon|image|header|label|badge|tooltip|tag|menuitem|cell|column|uploader|date-picker|key)\b$/i);
179
184
  elementType = finalWordMatch ? finalWordMatch[1].toLowerCase() : undefined;
180
185
  }
181
186
 
187
+ // Q2: a second [ref] after "contain"/"include" = the child element every container
188
+ // must hold, e.g. "see all [Product Card] contain [Add To Cart] button".
189
+ let childRef: string | undefined;
190
+ let childElementType: string | undefined;
191
+ const childMatch = text.match(/\b(?:contain|contains|include|includes)\s+\[([^\]]+)\]\s*([\w-]+)?/i);
192
+ if (childMatch) {
193
+ childRef = childMatch[1];
194
+ childElementType = childMatch[2] ? childMatch[2].toLowerCase() : undefined;
195
+ }
196
+
182
197
  // Extract positional index from "[Element] field 3", "[Element] button 2", etc.
183
198
  let nth = 0;
184
199
  if (selectorRef && selectorMatch) {
@@ -202,6 +217,8 @@ export class GherkinParser {
202
217
  keyword: step.keyword.trim(),
203
218
  text: step.text, // Preserve original text (with parent scoping) for pattern matching
204
219
  selectorRef,
220
+ childRef,
221
+ childElementType,
205
222
  dataRef,
206
223
  value,
207
224
  elementType,
@@ -0,0 +1,8 @@
1
+ {
2
+ const __cards_{{stepCounter}} = {{> locator}};
3
+ const __n_{{stepCounter}} = await __cards_{{stepCounter}}.count();
4
+ expect(__n_{{stepCounter}}, 'Expected at least one [{{selectorRef}}]').toBeGreaterThan(0);
5
+ for (let __i_{{stepCounter}} = 0; __i_{{stepCounter}} < __n_{{stepCounter}}; __i_{{stepCounter}}++) {
6
+ await expect({{#with child}}{{> locator}}{{/with}}, 'Every [{{selectorRef}}] must contain [{{childRef}}]').toBeVisible();
7
+ }
8
+ }
@@ -13,6 +13,11 @@ import { StepPattern, StepTemplateData } from './types';
13
13
  * 2. List: `User see all [Product Name] contain {{selected_category}}`
14
14
  * → asserts EVERY matching element's text contains the value
15
15
  * (e.g. all products belong to the selected category/brand).
16
+ *
17
+ * 3. All-card: `User see all [Product Card] contain [Add To Cart] button`
18
+ * → asserts EVERY container element holds the child element
19
+ * (structural per-card proof: every card has name/price/action).
20
+ * Roadmap Q2 — the fix for "each card exposes X" claims.
16
21
  */
17
22
  export const capturePatterns: StepPattern[] = [
18
23
  {
@@ -56,4 +61,37 @@ export const capturePatterns: StepPattern[] = [
56
61
  },
57
62
  priority: 34,
58
63
  },
64
+ {
65
+ // Q2 — all-card structural assertion: every container holds the child element.
66
+ // Distinguished from `all-contain-assertion` by a second [ref] (childRef) and the
67
+ // ABSENCE of a value/data (which would make it a text-contains assertion instead).
68
+ name: 'all-contain-element',
69
+ matcher: (step: ParsedStep) =>
70
+ /\b(see|sees)\b/i.test(step.text) &&
71
+ /\ball\b/i.test(step.text) &&
72
+ /(contain|contains|include|includes)/i.test(step.text) &&
73
+ !!step.selectorRef && !!step.childRef && !step.value && !step.dataRef,
74
+ resolver: (step, context): StepTemplateData => {
75
+ const container = context.selectorResolver.resolveSelector(
76
+ step.selectorRef!, undefined, step.elementType, step.nth,
77
+ );
78
+ const child = context.selectorResolver.resolveSelector(
79
+ step.childRef!, undefined, step.childElementType, 0,
80
+ );
81
+ const sc = context.stepCounter;
82
+ return {
83
+ templateName: 'all-contain-element',
84
+ data: {
85
+ ...container,
86
+ selectorRef: step.selectorRef,
87
+ childRef: step.childRef,
88
+ stepCounter: sc,
89
+ // Render the child RELATIVE to each container row via locator.hbs's parentLocator branch.
90
+ child: { ...child, parentLocator: `__cards_${sc}.nth(__i_${sc})` },
91
+ },
92
+ comment: `Assert all ${step.selectorRef} contain [${step.childRef}]`,
93
+ };
94
+ },
95
+ priority: 36,
96
+ },
59
97
  ];
@@ -9,16 +9,19 @@
9
9
  import * as path from 'path';
10
10
  import { loadScenarios, parseViewpointOverview, ScenarioInfo, ViewpointEntry } from './parse';
11
11
  import {
12
- loadCatalog, viewpointGate, assertionDepth, dataThemesFor, coverageBalance, duplicateClusters, traceability,
13
- GateResult, DepthResult, BalanceResult, DuplicateResult, TraceResult,
12
+ loadCatalog, viewpointGate, assertionDepth, dataThemesFor, coverageBalance, duplicateClusters, traceability, claimProof, taxonomyLint,
13
+ GateResult, DepthResult, BalanceResult, DuplicateResult, TraceResult, ClaimProofResult, TaxonomyResult,
14
14
  } from './sensors';
15
15
  import { readIntent, projectRootFromScreenDir, IntentProfile } from './intent';
16
+ import { getProvenance, Provenance } from './provenance';
16
17
 
17
18
  export interface AuditReport {
18
19
  screen: string;
19
20
  scenarioCount: number;
20
21
  gate: GateResult;
21
22
  depth: DepthResult;
23
+ claim: ClaimProofResult;
24
+ taxonomy: TaxonomyResult;
22
25
  balance: BalanceResult;
23
26
  duplicates: DuplicateResult;
24
27
  trace: TraceResult;
@@ -33,6 +36,7 @@ export interface AuditReport {
33
36
  gateStatus: 'PASS' | 'FAIL';
34
37
  findings: string[]; // human-actionable, what the Repair loop would target
35
38
  intent: IntentProfile; // P3 — the intent profile that drove the thresholds
39
+ provenance: Provenance; // D1 — sungen version + catalog hash (diagnose cross-user score gaps)
36
40
  }
37
41
 
38
42
  export function runAudit(screenDir: string, screenName: string): AuditReport {
@@ -47,6 +51,8 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
47
51
  // P3 — intent profile from qa/context.md drives the depth threshold (focus).
48
52
  const intent = readIntent(projectRootFromScreenDir(screenDir));
49
53
  const depth = assertionDepth(scenarios, dataThemesFor(catalog, gate.pageType), intent.focus);
54
+ const claim = claimProof(scenarios, intent.focus);
55
+ const taxonomy = taxonomyLint(scenarios);
50
56
  const balance = coverageBalance(scenarios);
51
57
  const duplicates = duplicateClusters(scenarios);
52
58
  const trace = traceability(scenarios, viewpoints);
@@ -78,6 +84,13 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
78
84
  `add data assertions (\`... with {{value}}\`, \`see all ... contain {{v}}\`) or, if cross-screen, defer to a flow with @manual + reason.`,
79
85
  );
80
86
  }
87
+ for (const u of claim.unproven) {
88
+ const tag = u.severity === 'fail' ? 'CLAIM-UNPROVEN' : 'CLAIM-WEAK';
89
+ findings.push(`${tag}: "${u.name}" — title claims [${u.claim}] but steps lack ${u.need}. ${u.hint}`);
90
+ }
91
+ for (const m of taxonomy.mislabeled) {
92
+ findings.push(`VP-MISLABEL: "${m.name}" is coded VP-${m.current} but reads as ${m.suggested} (signal: "${m.signal}") → re-tag VP-${m.suggested}-NNN so the coverage matrix isn't skewed.`);
93
+ }
81
94
  if (balance.imbalanced) {
82
95
  findings.push(`BALANCE: ${balance.note} Stop expanding secondary viewpoints until business-core gaps are filled.`);
83
96
  }
@@ -91,12 +104,12 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
91
104
  // Gate now spans coverage (viewpoint themes) AND depth (data-correctness).
92
105
  // A depth 'fail' (below the intent threshold) fails the gate; 'warn' does not.
93
106
  const gateStatus: 'PASS' | 'FAIL' =
94
- gate.gaps.length === 0 && depth.verdict !== 'fail' ? 'PASS' : 'FAIL';
107
+ gate.gaps.length === 0 && depth.verdict !== 'fail' && claim.verdict !== 'fail' ? 'PASS' : 'FAIL';
95
108
 
96
109
  return {
97
110
  screen: screenName,
98
111
  scenarioCount: scenarios.length,
99
- gate, depth, balance, duplicates, trace,
112
+ gate, depth, claim, taxonomy, balance, duplicates, trace,
100
113
  score: {
101
114
  overall: Math.round(overall * 10) / 10,
102
115
  coverage: Math.round(coverage * 100) / 100,
@@ -108,5 +121,6 @@ export function runAudit(screenDir: string, screenName: string): AuditReport {
108
121
  gateStatus,
109
122
  findings,
110
123
  intent,
124
+ provenance: getProvenance(),
111
125
  };
112
126
  }
@@ -28,6 +28,7 @@ export interface ScenarioInfo {
28
28
  shallow: boolean; // asserts only visibility/navigation (no data)
29
29
  stepSkeleton: string; // normalized steps for duplicate clustering
30
30
  haystack: string; // lowercase name + steps text (for keyword coverage)
31
+ stepsText: string; // lowercase steps ONLY (name excluded) — for claim-proof
31
32
  }
32
33
 
33
34
  // ---------- test-viewpoint.md ----------
@@ -98,11 +99,13 @@ function classifyScenario(sc: ParsedScenario): ScenarioInfo {
98
99
  let hasData = false;
99
100
  const skeletonParts: string[] = [];
100
101
  const textParts: string[] = [sc.name];
102
+ const stepTextParts: string[] = [];
101
103
 
102
104
  for (const step of sc.steps as ParsedStep[]) {
103
105
  const kw = step.keyword.trim();
104
106
  if (kw === 'Given' || kw === 'When' || kw === 'Then') last = kw;
105
107
  textParts.push(step.text);
108
+ stepTextParts.push(step.text);
106
109
  // normalized skeleton: keep [refs] (distinct targets = distinct tests),
107
110
  // but neutralize {{vars}} and quoted values so EP/data families collapse.
108
111
  const skel = step.text
@@ -132,6 +135,7 @@ function classifyScenario(sc: ParsedScenario): ScenarioInfo {
132
135
  shallow,
133
136
  stepSkeleton: skeletonParts.join(' | '),
134
137
  haystack: textParts.join(' ').toLowerCase(),
138
+ stepsText: stepTextParts.join(' ').toLowerCase(),
135
139
  };
136
140
  }
137
141
 
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Provenance stamp for deterministic reports (score-determinism D1).
3
+ *
4
+ * The audit score is a pure function of the project files + the bundled catalog.
5
+ * Stamping the sungen version + a catalog hash into the report lets two users who
6
+ * get different scores see IMMEDIATELY whether the cause is a version/catalog
7
+ * mismatch (the main residual source of cross-user variance) rather than a real
8
+ * difference in their test design.
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import * as crypto from 'crypto';
13
+
14
+ export interface Provenance {
15
+ sungenVersion: string;
16
+ catalogHash: string; // sha256(universal-viewpoints.yaml), first 12 hex — the catalog that drives the score
17
+ }
18
+
19
+ export function getProvenance(): Provenance {
20
+ let sungenVersion = 'unknown';
21
+ try {
22
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf-8'));
23
+ if (pkg && typeof pkg.version === 'string') sungenVersion = pkg.version;
24
+ } catch { /* version stays 'unknown' */ }
25
+
26
+ let catalogHash = 'unknown';
27
+ try {
28
+ const cat = fs.readFileSync(path.join(__dirname, 'catalog', 'universal-viewpoints.yaml'), 'utf-8');
29
+ catalogHash = crypto.createHash('sha256').update(cat).digest('hex').slice(0, 12);
30
+ } catch { /* hash stays 'unknown' */ }
31
+
32
+ return { sungenVersion, catalogHash };
33
+ }