@sun-asterisk/sungen 2.7.0-beta.1 → 3.0.0-beta.71

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 (245) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/commands/add.js +3 -3
  3. package/dist/cli/commands/add.js.map +1 -1
  4. package/dist/cli/commands/audit.d.ts +3 -0
  5. package/dist/cli/commands/audit.d.ts.map +1 -0
  6. package/dist/cli/commands/audit.js +134 -0
  7. package/dist/cli/commands/audit.js.map +1 -0
  8. package/dist/cli/commands/blindspot.d.ts +3 -0
  9. package/dist/cli/commands/blindspot.d.ts.map +1 -0
  10. package/dist/cli/commands/blindspot.js +58 -0
  11. package/dist/cli/commands/blindspot.js.map +1 -0
  12. package/dist/cli/commands/challenge.d.ts +3 -0
  13. package/dist/cli/commands/challenge.d.ts.map +1 -0
  14. package/dist/cli/commands/challenge.js +102 -0
  15. package/dist/cli/commands/challenge.js.map +1 -0
  16. package/dist/cli/commands/feedback.d.ts +3 -0
  17. package/dist/cli/commands/feedback.d.ts.map +1 -0
  18. package/dist/cli/commands/feedback.js +72 -0
  19. package/dist/cli/commands/feedback.js.map +1 -0
  20. package/dist/cli/commands/generate.d.ts.map +1 -1
  21. package/dist/cli/commands/generate.js +22 -0
  22. package/dist/cli/commands/generate.js.map +1 -1
  23. package/dist/cli/commands/ledger.d.ts +3 -0
  24. package/dist/cli/commands/ledger.d.ts.map +1 -0
  25. package/dist/cli/commands/ledger.js +71 -0
  26. package/dist/cli/commands/ledger.js.map +1 -0
  27. package/dist/cli/commands/manifest.d.ts +3 -0
  28. package/dist/cli/commands/manifest.d.ts.map +1 -0
  29. package/dist/cli/commands/manifest.js +101 -0
  30. package/dist/cli/commands/manifest.js.map +1 -0
  31. package/dist/cli/commands/script-check.d.ts +3 -0
  32. package/dist/cli/commands/script-check.d.ts.map +1 -0
  33. package/dist/cli/commands/script-check.js +97 -0
  34. package/dist/cli/commands/script-check.js.map +1 -0
  35. package/dist/cli/commands/trace.d.ts +3 -0
  36. package/dist/cli/commands/trace.d.ts.map +1 -0
  37. package/dist/cli/commands/trace.js +110 -0
  38. package/dist/cli/commands/trace.js.map +1 -0
  39. package/dist/cli/commands/update.d.ts.map +1 -1
  40. package/dist/cli/commands/update.js +22 -9
  41. package/dist/cli/commands/update.js.map +1 -1
  42. package/dist/cli/index.js +16 -0
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +1 -0
  45. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +7 -0
  46. package/dist/generators/test-generator/patterns/capture-patterns.d.ts +16 -0
  47. package/dist/generators/test-generator/patterns/capture-patterns.d.ts.map +1 -0
  48. package/dist/generators/test-generator/patterns/capture-patterns.js +54 -0
  49. package/dist/generators/test-generator/patterns/capture-patterns.js.map +1 -0
  50. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  51. package/dist/generators/test-generator/patterns/index.js +2 -0
  52. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  53. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  54. package/dist/generators/test-generator/step-mapper.js +1 -0
  55. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  56. package/dist/generators/test-generator/utils/data-resolver.d.ts +5 -0
  57. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  58. package/dist/generators/test-generator/utils/data-resolver.js +17 -0
  59. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  60. package/dist/harness/audit.d.ts +24 -0
  61. package/dist/harness/audit.d.ts.map +1 -0
  62. package/dist/harness/audit.js +115 -0
  63. package/dist/harness/audit.js.map +1 -0
  64. package/dist/harness/blindspot.d.ts +15 -0
  65. package/dist/harness/blindspot.d.ts.map +1 -0
  66. package/dist/harness/blindspot.js +85 -0
  67. package/dist/harness/blindspot.js.map +1 -0
  68. package/dist/harness/catalog/universal-viewpoints.yaml +114 -0
  69. package/dist/harness/challenge.d.ts +21 -0
  70. package/dist/harness/challenge.d.ts.map +1 -0
  71. package/dist/harness/challenge.js +151 -0
  72. package/dist/harness/challenge.js.map +1 -0
  73. package/dist/harness/feedback.d.ts +29 -0
  74. package/dist/harness/feedback.d.ts.map +1 -0
  75. package/dist/harness/feedback.js +106 -0
  76. package/dist/harness/feedback.js.map +1 -0
  77. package/dist/harness/intent.d.ts +11 -0
  78. package/dist/harness/intent.d.ts.map +1 -0
  79. package/dist/harness/intent.js +86 -0
  80. package/dist/harness/intent.js.map +1 -0
  81. package/dist/harness/ledger.d.ts +42 -0
  82. package/dist/harness/ledger.d.ts.map +1 -0
  83. package/dist/harness/ledger.js +171 -0
  84. package/dist/harness/ledger.js.map +1 -0
  85. package/dist/harness/manifest.d.ts +42 -0
  86. package/dist/harness/manifest.d.ts.map +1 -0
  87. package/dist/harness/manifest.js +209 -0
  88. package/dist/harness/manifest.js.map +1 -0
  89. package/dist/harness/parse.d.ts +22 -0
  90. package/dist/harness/parse.d.ts.map +1 -0
  91. package/dist/harness/parse.js +163 -0
  92. package/dist/harness/parse.js.map +1 -0
  93. package/dist/harness/script-check.d.ts +16 -0
  94. package/dist/harness/script-check.d.ts.map +1 -0
  95. package/dist/harness/script-check.js +169 -0
  96. package/dist/harness/script-check.js.map +1 -0
  97. package/dist/harness/secret-scan.d.ts +8 -0
  98. package/dist/harness/secret-scan.d.ts.map +1 -0
  99. package/dist/harness/secret-scan.js +88 -0
  100. package/dist/harness/secret-scan.js.map +1 -0
  101. package/dist/harness/sensors.d.ts +88 -0
  102. package/dist/harness/sensors.d.ts.map +1 -0
  103. package/dist/harness/sensors.js +232 -0
  104. package/dist/harness/sensors.js.map +1 -0
  105. package/dist/harness/trace.d.ts +31 -0
  106. package/dist/harness/trace.d.ts.map +1 -0
  107. package/dist/harness/trace.js +173 -0
  108. package/dist/harness/trace.js.map +1 -0
  109. package/dist/orchestrator/ai-rules-updater.d.ts +1 -0
  110. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
  111. package/dist/orchestrator/ai-rules-updater.js +55 -11
  112. package/dist/orchestrator/ai-rules-updater.js.map +1 -1
  113. package/dist/orchestrator/figma/spec-figma-renderer.d.ts +2 -2
  114. package/dist/orchestrator/figma/spec-figma-renderer.js +2 -2
  115. package/dist/orchestrator/figma/spec-figma-section-renderers.d.ts +1 -1
  116. package/dist/orchestrator/figma/spec-figma-section-renderers.js +1 -1
  117. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  118. package/dist/orchestrator/project-initializer.js +10 -6
  119. package/dist/orchestrator/project-initializer.js.map +1 -1
  120. package/dist/orchestrator/templates/ai-instructions/claude-agent-challenge.md +46 -0
  121. package/dist/orchestrator/templates/ai-instructions/claude-agent-discovery.md +32 -0
  122. package/dist/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +37 -0
  123. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-flow.md +3 -3
  124. package/dist/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
  125. package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +36 -12
  126. package/dist/orchestrator/templates/ai-instructions/claude-cmd-design.md +12 -0
  127. package/dist/orchestrator/templates/ai-instructions/claude-cmd-feedback.md +36 -0
  128. package/dist/orchestrator/templates/ai-instructions/claude-cmd-review.md +27 -30
  129. package/dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +4 -1
  130. package/dist/orchestrator/templates/ai-instructions/claude-config.md +1 -4
  131. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +82 -0
  132. package/dist/orchestrator/templates/ai-instructions/{github-skill-sungen-figma-source.md → claude-skill-capture-mode-figma-pat.md} +14 -48
  133. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +60 -0
  134. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +38 -0
  135. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture.md +35 -0
  136. package/dist/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +84 -0
  137. package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -1
  138. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +3 -3
  139. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
  140. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +18 -10
  141. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-design.md +13 -0
  142. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +24 -0
  143. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +20 -30
  144. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +2 -1
  145. package/dist/orchestrator/templates/ai-instructions/copilot-config.md +1 -4
  146. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +82 -0
  147. package/{src/orchestrator/templates/ai-instructions/claude-skill-figma-source.md → dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md} +14 -48
  148. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +60 -0
  149. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +38 -0
  150. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +35 -0
  151. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +84 -0
  152. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +1 -1
  153. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -1
  154. package/dist/orchestrator/templates/specs-test-data.ts +9 -0
  155. package/dist/tools/figma/figma-auth.d.ts +5 -2
  156. package/dist/tools/figma/figma-auth.d.ts.map +1 -1
  157. package/dist/tools/figma/figma-auth.js +19 -9
  158. package/dist/tools/figma/figma-auth.js.map +1 -1
  159. package/docs/orchestration-spec.md +267 -0
  160. package/package.json +10 -6
  161. package/src/cli/commands/add.ts +3 -3
  162. package/src/cli/commands/audit.ts +92 -0
  163. package/src/cli/commands/blindspot.ts +48 -0
  164. package/src/cli/commands/challenge.ts +55 -0
  165. package/src/cli/commands/feedback.ts +65 -0
  166. package/src/cli/commands/generate.ts +19 -0
  167. package/src/cli/commands/ledger.ts +61 -0
  168. package/src/cli/commands/manifest.ts +55 -0
  169. package/src/cli/commands/script-check.ts +50 -0
  170. package/src/cli/commands/trace.ts +60 -0
  171. package/src/cli/commands/update.ts +30 -10
  172. package/src/cli/index.ts +16 -0
  173. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +1 -0
  174. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +7 -0
  175. package/src/generators/test-generator/patterns/capture-patterns.ts +59 -0
  176. package/src/generators/test-generator/patterns/index.ts +2 -0
  177. package/src/generators/test-generator/step-mapper.ts +1 -0
  178. package/src/generators/test-generator/utils/data-resolver.ts +20 -0
  179. package/src/harness/audit.ts +112 -0
  180. package/src/harness/blindspot.ts +51 -0
  181. package/src/harness/catalog/universal-viewpoints.yaml +114 -0
  182. package/src/harness/challenge.ts +131 -0
  183. package/src/harness/feedback.ts +84 -0
  184. package/src/harness/intent.ts +58 -0
  185. package/src/harness/ledger.ts +155 -0
  186. package/src/harness/manifest.ts +173 -0
  187. package/src/harness/parse.ts +145 -0
  188. package/src/harness/script-check.ts +149 -0
  189. package/src/harness/secret-scan.ts +51 -0
  190. package/src/harness/sensors.ts +279 -0
  191. package/src/harness/trace.ts +138 -0
  192. package/src/orchestrator/ai-rules-updater.ts +57 -10
  193. package/src/orchestrator/figma/spec-figma-renderer.ts +2 -2
  194. package/src/orchestrator/figma/spec-figma-section-renderers.ts +1 -1
  195. package/src/orchestrator/project-initializer.ts +10 -7
  196. package/src/orchestrator/templates/ai-instructions/claude-agent-challenge.md +46 -0
  197. package/src/orchestrator/templates/ai-instructions/claude-agent-discovery.md +32 -0
  198. package/src/orchestrator/templates/ai-instructions/claude-agent-reviewer.md +37 -0
  199. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-flow.md +3 -3
  200. package/src/orchestrator/templates/ai-instructions/claude-cmd-add-screen.md +5 -5
  201. package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +36 -12
  202. package/src/orchestrator/templates/ai-instructions/claude-cmd-design.md +12 -0
  203. package/src/orchestrator/templates/ai-instructions/claude-cmd-feedback.md +36 -0
  204. package/src/orchestrator/templates/ai-instructions/claude-cmd-review.md +27 -30
  205. package/src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md +4 -1
  206. package/src/orchestrator/templates/ai-instructions/claude-config.md +1 -4
  207. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +82 -0
  208. package/{dist/orchestrator/templates/ai-instructions/copilot-skill-figma-source.md → src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-pat.md} +14 -48
  209. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +60 -0
  210. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +38 -0
  211. package/src/orchestrator/templates/ai-instructions/claude-skill-capture.md +35 -0
  212. package/src/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md +84 -0
  213. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +40 -1
  214. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +3 -3
  215. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +4 -4
  216. package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +18 -10
  217. package/src/orchestrator/templates/ai-instructions/copilot-cmd-design.md +13 -0
  218. package/src/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +24 -0
  219. package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +20 -30
  220. package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +2 -1
  221. package/src/orchestrator/templates/ai-instructions/copilot-config.md +1 -4
  222. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +82 -0
  223. package/{dist/orchestrator/templates/ai-instructions/claude-skill-figma-source.md → src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md} +14 -48
  224. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +60 -0
  225. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +38 -0
  226. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +35 -0
  227. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +84 -0
  228. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md +1 -1
  229. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +40 -1
  230. package/src/orchestrator/templates/specs-test-data.ts +9 -0
  231. package/src/tools/figma/figma-auth.ts +20 -9
  232. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +0 -142
  233. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +0 -112
  234. package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +0 -73
  235. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +0 -142
  236. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +0 -112
  237. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +0 -73
  238. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-figma.md +0 -142
  239. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-live.md +0 -112
  240. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-local.md +0 -73
  241. package/src/orchestrator/templates/ai-instructions/copilot-skill-figma-source.md +0 -151
  242. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-figma.md +0 -142
  243. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-live.md +0 -112
  244. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-local.md +0 -73
  245. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-figma-source.md +0 -151
@@ -0,0 +1,16 @@
1
+ export interface ScriptCheckResult {
2
+ screen: string;
3
+ specPath: string | null;
4
+ automatedScenarios: number;
5
+ manualScenarios: number;
6
+ specTestBlocks: number;
7
+ countMatch: boolean;
8
+ missingInSpec: string[];
9
+ extraInSpec: string[];
10
+ drift: 'in-sync' | 'drift' | 'no-spec';
11
+ driftHunks: string[];
12
+ status: 'OK' | 'FAIL';
13
+ findings: string[];
14
+ }
15
+ export declare function runScriptCheck(screenDir: string, screenName: string, flowMode: boolean): Promise<ScriptCheckResult>;
16
+ //# sourceMappingURL=script-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-check.d.ts","sourceRoot":"","sources":["../../src/harness/script-check.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACvC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAoCD,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgFzH"}
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runScriptCheck = runScriptCheck;
37
+ /**
38
+ * Script-check — verify the generated Playwright spec is a faithful 1:1 of the
39
+ * Gherkin feature, i.e. "the testcase and the test code are not two different things".
40
+ *
41
+ * Two deterministic checks:
42
+ * A. Structural 1:1 — every non-@manual / non-@steps scenario has exactly one
43
+ * matching `test('<title>')` block in the committed spec (and no extras).
44
+ * B. Drift — regenerate the spec from the SAME .feature + selectors + test-data
45
+ * into a temp dir and diff against the committed spec. Any difference means
46
+ * the committed spec was hand-edited or is stale (feature changed without a
47
+ * regenerate) → the script no longer reflects the testcase.
48
+ *
49
+ * Pure-deterministic (reuses the compiler). No AI.
50
+ */
51
+ const fs = __importStar(require("fs"));
52
+ const path = __importStar(require("path"));
53
+ const os = __importStar(require("os"));
54
+ const parse_1 = require("./parse");
55
+ function extractTestTitles(specSrc) {
56
+ // Count real test cases only: test(...), test.only/.skip/.fixme(...).
57
+ // Exclude test.describe / test.beforeAll / hooks (not test cases).
58
+ const titles = [];
59
+ const re = /\btest(?:\.(?:only|skip|fixme))?\(\s*(['"`])([^'"`]+)\1/g;
60
+ let m;
61
+ while ((m = re.exec(specSrc)))
62
+ titles.push(m[2].trim());
63
+ return titles;
64
+ }
65
+ function normalize(src) {
66
+ return src
67
+ .split('\n')
68
+ .map((l) => l.replace(/\s+$/, ''))
69
+ .join('\n')
70
+ .replace(/\n{3,}/g, '\n\n')
71
+ .trim();
72
+ }
73
+ function findSpec(dir, screen) {
74
+ // generated spec: <dir>/<screen>/<feature>.spec.ts (or nested)
75
+ const hits = [];
76
+ const walk = (d) => {
77
+ if (!fs.existsSync(d))
78
+ return;
79
+ for (const e of fs.readdirSync(d, { withFileTypes: true })) {
80
+ const p = path.join(d, e.name);
81
+ if (e.isDirectory())
82
+ walk(p);
83
+ else if (e.name.endsWith('.spec.ts'))
84
+ hits.push(p);
85
+ }
86
+ };
87
+ walk(dir);
88
+ return hits[0] ?? null;
89
+ }
90
+ async function runScriptCheck(screenDir, screenName, flowMode) {
91
+ const featurePath = path.join(screenDir, 'features', `${screenName}.feature`);
92
+ const scenarios = (0, parse_1.loadScenarios)(featurePath);
93
+ const automated = scenarios.filter((s) => !s.manual);
94
+ const manual = scenarios.filter((s) => s.manual);
95
+ const committedSpec = findSpec(path.join(process.cwd(), 'specs', 'generated'), screenName);
96
+ const findings = [];
97
+ let specTitles = [];
98
+ let specSrc = '';
99
+ if (committedSpec) {
100
+ specSrc = fs.readFileSync(committedSpec, 'utf-8');
101
+ specTitles = extractTestTitles(specSrc);
102
+ }
103
+ else {
104
+ findings.push('No generated spec found under specs/generated/ — run `sungen generate` / `/sungen:run-test` first.');
105
+ }
106
+ // A. Structural 1:1
107
+ const specTitleSet = new Set(specTitles);
108
+ const scenTitleSet = new Set(automated.map((s) => s.name));
109
+ const missingInSpec = automated.filter((s) => !specTitleSet.has(s.name)).map((s) => s.name);
110
+ const extraInSpec = specTitles.filter((t) => !scenTitleSet.has(t));
111
+ const countMatch = committedSpec ? automated.length === specTitles.length : false;
112
+ if (committedSpec && !countMatch) {
113
+ findings.push(`Count mismatch: ${automated.length} automated scenarios vs ${specTitles.length} test() blocks.`);
114
+ }
115
+ for (const t of missingInSpec)
116
+ findings.push(`MISSING in spec: scenario "${t}" has no test() block (stale spec — regenerate).`);
117
+ for (const t of extraInSpec)
118
+ findings.push(`EXTRA in spec: test "${t}" has no matching scenario (hand-edited spec).`);
119
+ // B. Drift — regenerate to temp and diff
120
+ let drift = committedSpec ? 'in-sync' : 'no-spec';
121
+ const driftHunks = [];
122
+ if (committedSpec) {
123
+ try {
124
+ const { CodeGenerator } = require('../generators/test-generator/code-generator');
125
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'sungen-scriptcheck-'));
126
+ const qaSourceDir = path.join(process.cwd(), 'qa', flowMode ? 'flows' : 'screens');
127
+ const gen = new CodeGenerator({ framework: 'playwright', screenName, runtimeData: true, flowMode });
128
+ await gen.generateAllTests(qaSourceDir, tmp, [featurePath]);
129
+ const fresh = findSpec(tmp, screenName);
130
+ if (fresh) {
131
+ const a = normalize(specSrc);
132
+ const b = normalize(fs.readFileSync(fresh, 'utf-8'));
133
+ if (a !== b) {
134
+ drift = 'drift';
135
+ // collect a few differing lines
136
+ const al = a.split('\n'), bl = b.split('\n');
137
+ const max = Math.max(al.length, bl.length);
138
+ for (let i = 0, shown = 0; i < max && shown < 6; i++) {
139
+ if (al[i] !== bl[i]) {
140
+ driftHunks.push(` L${i + 1}\n committed: ${(al[i] ?? '∅').trim().slice(0, 100)}\n expected : ${(bl[i] ?? '∅').trim().slice(0, 100)}`);
141
+ shown++;
142
+ }
143
+ }
144
+ findings.push('DRIFT: committed spec differs from a fresh regenerate → spec was hand-edited or the .feature changed without `sungen generate`. The test code no longer reflects the Gherkin.');
145
+ }
146
+ }
147
+ fs.rmSync(tmp, { recursive: true, force: true });
148
+ }
149
+ catch (e) {
150
+ findings.push(`Drift check skipped (regenerate failed): ${e instanceof Error ? e.message : e}`);
151
+ }
152
+ }
153
+ const ok = !!committedSpec && countMatch && missingInSpec.length === 0 && extraInSpec.length === 0 && drift === 'in-sync';
154
+ return {
155
+ screen: screenName,
156
+ specPath: committedSpec,
157
+ automatedScenarios: automated.length,
158
+ manualScenarios: manual.length,
159
+ specTestBlocks: specTitles.length,
160
+ countMatch,
161
+ missingInSpec,
162
+ extraInSpec,
163
+ drift,
164
+ driftHunks,
165
+ status: ok ? 'OK' : 'FAIL',
166
+ findings,
167
+ };
168
+ }
169
+ //# sourceMappingURL=script-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"script-check.js","sourceRoot":"","sources":["../../src/harness/script-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface SecretHit {
2
+ file: string;
3
+ line: number;
4
+ reason: string;
5
+ }
6
+ /** Scan a screen/flow dir's test-data/*.yaml for likely real secrets. */
7
+ export declare function scanTestDataSecrets(baseDir: string): SecretHit[];
8
+ //# sourceMappingURL=secret-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scan.d.ts","sourceRoot":"","sources":["../../src/harness/secret-scan.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AA4BzE,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAUhE"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.scanTestDataSecrets = scanTestDataSecrets;
37
+ /**
38
+ * Secret scanner (security S0) — warn (never block) when test-data appears to
39
+ * contain a REAL secret rather than a safe placeholder/test value.
40
+ *
41
+ * test-data/*.yaml is committed by design, so a real API key / prod token there is
42
+ * a leak waiting to happen. We flag high-confidence signals only, to avoid nagging
43
+ * about ordinary test passwords ("Test@123") or `{{vars}}`.
44
+ */
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ // High-confidence vendor token prefixes (very low false-positive rate).
48
+ const VENDOR_PREFIXES = /\b(figd_[A-Za-z0-9_-]{12,}|ghp_[A-Za-z0-9]{20,}|gho_[A-Za-z0-9]{20,}|glpat-[A-Za-z0-9_-]{16,}|sk-[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{12,}|xox[baprs]-[A-Za-z0-9-]{10,}|AIza[0-9A-Za-z_-]{30,})/;
49
+ // A secret-named key assigned a long, non-placeholder value.
50
+ const SECRET_KEY = /\b(password|passwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|client[_-]?secret)\b\s*:\s*(.+)$/i;
51
+ const PLACEHOLDERish = /\{\{|<.*>|changeme|example|dummy|test\b|placeholder|xxxx|\*\*\*/i;
52
+ function scanText(text, file) {
53
+ const hits = [];
54
+ text.split('\n').forEach((raw, i) => {
55
+ const line = i + 1;
56
+ if (VENDOR_PREFIXES.test(raw)) {
57
+ hits.push({ file, line, reason: 'looks like a real vendor API token/key' });
58
+ return;
59
+ }
60
+ const m = raw.match(SECRET_KEY);
61
+ if (m) {
62
+ const val = m[2].trim().replace(/^["']|["']$/g, '');
63
+ // Long, high-entropy-ish, not an obvious placeholder/test value.
64
+ if (val.length >= 20 && !PLACEHOLDERish.test(raw) && /[A-Za-z]/.test(val) && /[0-9]/.test(val)) {
65
+ hits.push({ file, line, reason: `secret-named key "${m[1]}" with a long literal value` });
66
+ }
67
+ }
68
+ });
69
+ return hits;
70
+ }
71
+ /** Scan a screen/flow dir's test-data/*.yaml for likely real secrets. */
72
+ function scanTestDataSecrets(baseDir) {
73
+ const tdDir = path.join(baseDir, 'test-data');
74
+ if (!fs.existsSync(tdDir))
75
+ return [];
76
+ const hits = [];
77
+ for (const f of fs.readdirSync(tdDir)) {
78
+ if (!/\.ya?ml$/i.test(f))
79
+ continue;
80
+ const p = path.join(tdDir, f);
81
+ try {
82
+ hits.push(...scanText(fs.readFileSync(p, 'utf-8'), path.join('test-data', f)));
83
+ }
84
+ catch { /* ignore */ }
85
+ }
86
+ return hits;
87
+ }
88
+ //# sourceMappingURL=secret-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scan.js","sourceRoot":"","sources":["../../src/harness/secret-scan.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,kDAUC;AAlDD;;;;;;;GAOG;AACH,uCAAyB;AACzB,2CAA6B;AAI7B,wEAAwE;AACxE,MAAM,eAAe,GAAG,0LAA0L,CAAC;AACnN,6DAA6D;AAC7D,MAAM,UAAU,GAAG,8GAA8G,CAAC;AAClI,MAAM,cAAc,GAAG,kEAAkE,CAAC;AAE1F,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAY;IAC1C,MAAM,IAAI,GAAgB,EAAE,CAAC;IAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QAClC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACnB,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,wCAAwC,EAAE,CAAC,CAAC;YAC5E,OAAO;QACT,CAAC;QACD,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;YACpD,iEAAiE;YACjE,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/F,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC,6BAA6B,EAAE,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yEAAyE;AACzE,SAAgB,mBAAmB,CAAC,OAAe;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,IAAI,GAAgB,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC9B,IAAI,CAAC;YAAC,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAChH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { ScenarioInfo, ViewpointEntry } from './parse';
2
+ export interface ThemeDepth {
3
+ requires: string;
4
+ cross_screen?: boolean;
5
+ keywords?: string[];
6
+ template?: string;
7
+ }
8
+ export interface CatalogTheme {
9
+ theme: string;
10
+ keywords: string[];
11
+ depth?: ThemeDepth;
12
+ }
13
+ export interface Catalog {
14
+ page_types: Record<string, {
15
+ detect_keywords: string[];
16
+ must_cover: CatalogTheme[];
17
+ }>;
18
+ universal: CatalogTheme[];
19
+ }
20
+ export declare function loadCatalog(): Catalog;
21
+ export interface GateResult {
22
+ pageType: string | null;
23
+ themesTotal: number;
24
+ themesCovered: number;
25
+ coverageRatio: number;
26
+ gaps: {
27
+ theme: string;
28
+ keywords: string[];
29
+ status: 'missing' | 'shallow';
30
+ }[];
31
+ universalGaps: string[];
32
+ }
33
+ export declare function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEntry[], catalog: Catalog): GateResult;
34
+ export type DepthVerdict = 'pass' | 'warn' | 'fail';
35
+ export interface DepthResult {
36
+ total: number;
37
+ shallowTotal: number;
38
+ shallowRatio: number;
39
+ businessCriticalTotal: number;
40
+ businessCriticalShallow: number;
41
+ bcDepthRatio: number;
42
+ shallowBusinessCritical: {
43
+ name: string;
44
+ category?: string;
45
+ }[];
46
+ focus: string;
47
+ threshold: number;
48
+ verdict: DepthVerdict;
49
+ }
50
+ /**
51
+ * Depth = do DATA-correctness scenarios actually assert DATA (not just visibility)?
52
+ * "Depth-required" is CATALOG-DRIVEN: only scenarios matching a theme whose
53
+ * `depth.requires === 'data-assertion'` are measured. Navigation viewpoints
54
+ * (category NAV) are excluded — landing on a page IS their correct assertion.
55
+ * This avoids the old keyword-based false-positives (e.g. "API list page").
56
+ */
57
+ export declare function assertionDepth(scenarios: ScenarioInfo[], dataThemes?: CatalogTheme[], focus?: string): DepthResult;
58
+ /** Collect data-correctness themes (depth.requires) for a page-type + universal. */
59
+ export declare function dataThemesFor(catalog: Catalog, pageType: string | null): CatalogTheme[];
60
+ export interface BalanceResult {
61
+ byBucket: Record<string, number>;
62
+ byCategory: Record<string, number>;
63
+ coreCount: number;
64
+ secondaryCount: number;
65
+ imbalanced: boolean;
66
+ note: string;
67
+ }
68
+ export declare function coverageBalance(scenarios: ScenarioInfo[]): BalanceResult;
69
+ export interface DuplicateResult {
70
+ clusters: {
71
+ skeleton: string;
72
+ scenarios: string[];
73
+ sameDataLikely: boolean;
74
+ }[];
75
+ exactDuplicateCount: number;
76
+ sameShapeCount: number;
77
+ }
78
+ export declare function duplicateClusters(scenarios: ScenarioInfo[]): DuplicateResult;
79
+ export interface TraceResult {
80
+ total: number;
81
+ withVpCode: number;
82
+ mappedToOverview: number;
83
+ withVpCodeRatio: number;
84
+ mappedRatio: number;
85
+ note: string;
86
+ }
87
+ export declare function traceability(scenarios: ScenarioInfo[], viewpoints: ViewpointEntry[]): TraceResult;
88
+ //# sourceMappingURL=sensors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensors.d.ts","sourceRoot":"","sources":["../../src/harness/sensors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadCatalog = loadCatalog;
37
+ exports.viewpointGate = viewpointGate;
38
+ exports.assertionDepth = assertionDepth;
39
+ exports.dataThemesFor = dataThemesFor;
40
+ exports.coverageBalance = coverageBalance;
41
+ exports.duplicateClusters = duplicateClusters;
42
+ exports.traceability = traceability;
43
+ /**
44
+ * Harness Sensors — deterministic quality measurement over test-design artifacts.
45
+ * Each sensor returns a structured finding the Orchestrator/Repair loop can act on.
46
+ *
47
+ * NOTE (honesty): the Viewpoint Gate and Duplicate sensors involve semantics, so
48
+ * they are HEURISTIC (keyword / skeleton based), not provably exhaustive. They use
49
+ * a curated seed catalog as the matching reference. See docs/orchestration-spec.md §5.2.
50
+ */
51
+ const fs = __importStar(require("fs"));
52
+ const path = __importStar(require("path"));
53
+ const yaml_1 = require("yaml");
54
+ // Business-critical category codes (project VP-<CAT> prefixes). Configurable later.
55
+ const BUSINESS_CRITICAL_CATS = ['LIST', 'CART', 'PRODUCT', 'FILTER', 'CHECKOUT', 'ORDER'];
56
+ // Buckets for coverage-balance.
57
+ const BUCKETS = {
58
+ 'business-core': BUSINESS_CRITICAL_CATS,
59
+ 'presentation': ['UI'],
60
+ 'validation-security': ['VAL', 'SEC', 'SUB'],
61
+ 'behavior': ['LOGIC'],
62
+ 'navigation': ['NAV'],
63
+ };
64
+ function loadCatalog() {
65
+ const p = path.join(__dirname, 'catalog', 'universal-viewpoints.yaml');
66
+ return (0, yaml_1.parse)(fs.readFileSync(p, 'utf-8'));
67
+ }
68
+ const has = (haystacks, kw) => {
69
+ const k = kw.toLowerCase();
70
+ return haystacks.some((h) => h.includes(k));
71
+ };
72
+ function viewpointGate(scenarios, viewpoints, catalog) {
73
+ const haystacks = [
74
+ ...scenarios.map((s) => s.haystack),
75
+ ...viewpoints.map((v) => `${v.id} ${v.reason}`.toLowerCase()),
76
+ ];
77
+ // Detect page-type by detect_keywords hit count
78
+ let pageType = null;
79
+ let best = 0;
80
+ for (const [pt, def] of Object.entries(catalog.page_types)) {
81
+ const hits = def.detect_keywords.filter((k) => has(haystacks, k)).length;
82
+ if (hits > best) {
83
+ best = hits;
84
+ pageType = pt;
85
+ }
86
+ }
87
+ const gaps = [];
88
+ let total = 0, covered = 0;
89
+ if (pageType) {
90
+ for (const t of catalog.page_types[pageType].must_cover) {
91
+ total++;
92
+ // Scenarios that cover this theme by keyword (include @manual — a manual
93
+ // scenario with a real assertion still covers the viewpoint for design).
94
+ const coverers = scenarios.filter((s) => t.keywords.some((k) => s.haystack.includes(k.toLowerCase())));
95
+ const deep = coverers.some((s) => s.hasDataAssertion);
96
+ if (deep)
97
+ covered++;
98
+ else if (coverers.length > 0)
99
+ gaps.push({ theme: t.theme, keywords: t.keywords, status: 'shallow' });
100
+ else
101
+ gaps.push({ theme: t.theme, keywords: t.keywords, status: 'missing' });
102
+ }
103
+ }
104
+ const universalGaps = catalog.universal
105
+ .filter((t) => !t.keywords.some((k) => has(scenarios.map((s) => s.haystack), k)))
106
+ .map((t) => t.theme);
107
+ return {
108
+ pageType,
109
+ themesTotal: total,
110
+ themesCovered: covered,
111
+ coverageRatio: total ? covered / total : 1,
112
+ gaps,
113
+ universalGaps,
114
+ };
115
+ }
116
+ // Intent → required depth ratio + whether a miss WARNs (smoke) or FAILs the gate.
117
+ // P3 will read `focus` from qa/context.md; P1 uses 'functional' by default.
118
+ const DEPTH_THRESHOLDS = {
119
+ functional: 0.7, 'e-commerce': 0.7, security: 0.8, smoke: 0.4,
120
+ };
121
+ const WARN_ONLY_FOCUS = new Set(['smoke']);
122
+ /**
123
+ * Depth = do DATA-correctness scenarios actually assert DATA (not just visibility)?
124
+ * "Depth-required" is CATALOG-DRIVEN: only scenarios matching a theme whose
125
+ * `depth.requires === 'data-assertion'` are measured. Navigation viewpoints
126
+ * (category NAV) are excluded — landing on a page IS their correct assertion.
127
+ * This avoids the old keyword-based false-positives (e.g. "API list page").
128
+ */
129
+ function assertionDepth(scenarios, dataThemes = [], focus = 'functional') {
130
+ const nonManual = scenarios.filter((s) => !s.manual);
131
+ const shallow = nonManual.filter((s) => s.shallow);
132
+ // Precise depth keywords come from each data-theme's depth.keywords (fallback: theme keywords).
133
+ const depthKeywords = dataThemes.flatMap((t) => (t.depth?.keywords?.length ? t.depth.keywords : t.keywords));
134
+ // Dismiss/close behaviors ("Continue Shopping closes the modal", "Escape dismisses the
135
+ // dialog") assert a HIDDEN state — that IS the correct, data-light assertion for the
136
+ // behavior. They only match a data keyword incidentally (e.g. "added"), so exclude them.
137
+ const isDismissBehavior = (s) => /\bis hidden\b|\bare hidden\b|\bdismiss|\bdisappear|\bno longer (visible|shown|present|displayed)\b|\bcloses? the (modal|dialog|popup|overlay|panel|menu)\b/.test(s.haystack);
138
+ const isDepthRequired = (s) => s.category !== 'NAV' && !isDismissBehavior(s) && depthKeywords.some((k) => s.haystack.includes(k.toLowerCase()));
139
+ const required = nonManual.filter(isDepthRequired);
140
+ const reqShallow = required.filter((s) => s.shallow);
141
+ // No data-theme scenarios on this screen → depth is not the binding constraint
142
+ // (the viewpoint gate already flags missing data themes). Don't double-penalize.
143
+ const ratio = required.length ? 1 - reqShallow.length / required.length : 1;
144
+ const threshold = DEPTH_THRESHOLDS[focus] ?? DEPTH_THRESHOLDS.functional;
145
+ let verdict = 'pass';
146
+ if (ratio < threshold)
147
+ verdict = WARN_ONLY_FOCUS.has(focus) ? 'warn' : 'fail';
148
+ return {
149
+ total: nonManual.length,
150
+ shallowTotal: shallow.length,
151
+ shallowRatio: nonManual.length ? shallow.length / nonManual.length : 0,
152
+ businessCriticalTotal: required.length,
153
+ businessCriticalShallow: reqShallow.length,
154
+ bcDepthRatio: ratio,
155
+ shallowBusinessCritical: reqShallow.map((s) => ({ name: s.name, category: s.category })),
156
+ focus,
157
+ threshold,
158
+ verdict,
159
+ };
160
+ }
161
+ /** Collect data-correctness themes (depth.requires) for a page-type + universal. */
162
+ function dataThemesFor(catalog, pageType) {
163
+ const themes = [];
164
+ if (pageType && catalog.page_types[pageType])
165
+ themes.push(...catalog.page_types[pageType].must_cover);
166
+ themes.push(...catalog.universal);
167
+ return themes.filter((t) => t.depth?.requires === 'data-assertion');
168
+ }
169
+ function coverageBalance(scenarios) {
170
+ const byCategory = {};
171
+ const byBucket = {};
172
+ for (const b of Object.keys(BUCKETS))
173
+ byBucket[b] = 0;
174
+ byBucket['other'] = 0;
175
+ for (const s of scenarios) {
176
+ const cat = s.category || 'NONE';
177
+ byCategory[cat] = (byCategory[cat] || 0) + 1;
178
+ const bucket = Object.entries(BUCKETS).find(([, cats]) => cats.includes(cat))?.[0] || 'other';
179
+ byBucket[bucket]++;
180
+ }
181
+ const core = byBucket['business-core'];
182
+ const secondary = byBucket['presentation'] + byBucket['validation-security'];
183
+ const imbalanced = secondary > core * 1.5 && core > 0;
184
+ return {
185
+ byBucket,
186
+ byCategory,
187
+ coreCount: core,
188
+ secondaryCount: secondary,
189
+ imbalanced,
190
+ note: imbalanced
191
+ ? `Secondary viewpoints (presentation+validation/security = ${secondary}) outweigh business-core (${core}) by >1.5x.`
192
+ : 'Balanced.',
193
+ };
194
+ }
195
+ function duplicateClusters(scenarios) {
196
+ const map = new Map();
197
+ for (const s of scenarios) {
198
+ const arr = map.get(s.stepSkeleton) || [];
199
+ arr.push(s);
200
+ map.set(s.stepSkeleton, arr);
201
+ }
202
+ const clusters = [...map.entries()]
203
+ .filter(([, arr]) => arr.length > 1)
204
+ .map(([skeleton, arr]) => ({
205
+ skeleton: skeleton.length > 120 ? skeleton.slice(0, 117) + '...' : skeleton,
206
+ scenarios: arr.map((s) => s.name),
207
+ // Same skeleton with data placeholders → likely an EP/data family (intentional), not a true dup.
208
+ sameDataLikely: !skeleton.includes('{}'),
209
+ }));
210
+ return {
211
+ clusters,
212
+ exactDuplicateCount: clusters.filter((c) => c.sameDataLikely).reduce((n, c) => n + (c.scenarios.length - 1), 0),
213
+ sameShapeCount: clusters.reduce((n, c) => n + c.scenarios.length, 0),
214
+ };
215
+ }
216
+ function traceability(scenarios, viewpoints) {
217
+ const overviewIds = new Set(viewpoints.map((v) => v.id.toUpperCase()));
218
+ const withCode = scenarios.filter((s) => s.vpCode);
219
+ // A scenario maps to overview if its full VP code OR its category-derived id exists in overview.
220
+ const mapped = withCode.filter((s) => overviewIds.has(s.vpCode) || [...overviewIds].some((id) => id.includes(s.category || '###')));
221
+ return {
222
+ total: scenarios.length,
223
+ withVpCode: withCode.length,
224
+ mappedToOverview: mapped.length,
225
+ withVpCodeRatio: scenarios.length ? withCode.length / scenarios.length : 0,
226
+ mappedRatio: scenarios.length ? mapped.length / scenarios.length : 0,
227
+ note: mapped.length < withCode.length * 0.5
228
+ ? 'Scenarios use ad-hoc VP-<CAT>-NNN codes not linked to viewpoint-overview ids (weak traceability — see review Gate 4).'
229
+ : 'Traceable.',
230
+ };
231
+ }
232
+ //# sourceMappingURL=sensors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensors.js","sourceRoot":"","sources":["../../src/harness/sensors.ts"],"names":[],"mappings":"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"}