@sun-asterisk/sungen 3.2.32 → 3.2.34-beta.1

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 (503) hide show
  1. package/dist/cli/commands/audit.d.ts +5 -0
  2. package/dist/cli/commands/audit.d.ts.map +1 -1
  3. package/dist/cli/commands/audit.js +64 -11
  4. package/dist/cli/commands/audit.js.map +1 -1
  5. package/dist/cli/commands/capability.d.ts.map +1 -1
  6. package/dist/cli/commands/capability.js +40 -18
  7. package/dist/cli/commands/capability.js.map +1 -1
  8. package/dist/cli/commands/changed.d.ts.map +1 -1
  9. package/dist/cli/commands/changed.js +2 -3
  10. package/dist/cli/commands/changed.js.map +1 -1
  11. package/dist/cli/commands/dashboard.d.ts.map +1 -1
  12. package/dist/cli/commands/dashboard.js +2 -28
  13. package/dist/cli/commands/dashboard.js.map +1 -1
  14. package/dist/cli/commands/defect.d.ts +7 -0
  15. package/dist/cli/commands/defect.d.ts.map +1 -0
  16. package/dist/cli/commands/defect.js +68 -0
  17. package/dist/cli/commands/defect.js.map +1 -0
  18. package/dist/cli/commands/delivery.d.ts.map +1 -1
  19. package/dist/cli/commands/delivery.js +113 -59
  20. package/dist/cli/commands/delivery.js.map +1 -1
  21. package/dist/cli/commands/generate.d.ts.map +1 -1
  22. package/dist/cli/commands/generate.js +14 -7
  23. package/dist/cli/commands/generate.js.map +1 -1
  24. package/dist/cli/commands/results-merge.d.ts +9 -0
  25. package/dist/cli/commands/results-merge.d.ts.map +1 -1
  26. package/dist/cli/commands/results-merge.js +9 -12
  27. package/dist/cli/commands/results-merge.js.map +1 -1
  28. package/dist/cli/commands/results-sanitize.d.ts.map +1 -1
  29. package/dist/cli/commands/results-sanitize.js +2 -13
  30. package/dist/cli/commands/results-sanitize.js.map +1 -1
  31. package/dist/cli/commands/select.d.ts +12 -0
  32. package/dist/cli/commands/select.d.ts.map +1 -0
  33. package/dist/cli/commands/select.js +122 -0
  34. package/dist/cli/commands/select.js.map +1 -0
  35. package/dist/cli/commands/triage.d.ts +7 -0
  36. package/dist/cli/commands/triage.d.ts.map +1 -0
  37. package/dist/cli/commands/triage.js +98 -0
  38. package/dist/cli/commands/triage.js.map +1 -0
  39. package/dist/cli/commands/update.d.ts.map +1 -1
  40. package/dist/cli/commands/update.js +3 -0
  41. package/dist/cli/commands/update.js.map +1 -1
  42. package/dist/cli/commands/viewpoint.d.ts +7 -0
  43. package/dist/cli/commands/viewpoint.d.ts.map +1 -0
  44. package/dist/cli/commands/viewpoint.js +70 -0
  45. package/dist/cli/commands/viewpoint.js.map +1 -0
  46. package/dist/cli/index.js +13 -2
  47. package/dist/cli/index.js.map +1 -1
  48. package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
  49. package/dist/dashboard/snapshot-builder.js +6 -10
  50. package/dist/dashboard/snapshot-builder.js.map +1 -1
  51. package/dist/dashboard/templates/index.html +1 -1
  52. package/dist/dashboard/types.d.ts +1 -0
  53. package/dist/dashboard/types.d.ts.map +1 -1
  54. package/dist/exporters/csv-exporter.d.ts +4 -0
  55. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  56. package/dist/exporters/csv-exporter.js +12 -11
  57. package/dist/exporters/csv-exporter.js.map +1 -1
  58. package/dist/exporters/feature-parser.d.ts +29 -0
  59. package/dist/exporters/feature-parser.d.ts.map +1 -1
  60. package/dist/exporters/feature-parser.js +44 -1
  61. package/dist/exporters/feature-parser.js.map +1 -1
  62. package/dist/exporters/json-exporter.d.ts.map +1 -1
  63. package/dist/exporters/json-exporter.js +7 -0
  64. package/dist/exporters/json-exporter.js.map +1 -1
  65. package/dist/exporters/locale-variants.d.ts +7 -1
  66. package/dist/exporters/locale-variants.d.ts.map +1 -1
  67. package/dist/exporters/locale-variants.js +23 -16
  68. package/dist/exporters/locale-variants.js.map +1 -1
  69. package/dist/exporters/manual-hybrid-oracle.d.ts +54 -0
  70. package/dist/exporters/manual-hybrid-oracle.d.ts.map +1 -0
  71. package/dist/exporters/manual-hybrid-oracle.js +114 -0
  72. package/dist/exporters/manual-hybrid-oracle.js.map +1 -0
  73. package/dist/exporters/matrix/build.d.ts.map +1 -1
  74. package/dist/exporters/matrix/build.js +68 -19
  75. package/dist/exporters/matrix/build.js.map +1 -1
  76. package/dist/exporters/matrix/carry-manual-results.d.ts +89 -0
  77. package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -0
  78. package/dist/exporters/matrix/carry-manual-results.js +302 -0
  79. package/dist/exporters/matrix/carry-manual-results.js.map +1 -0
  80. package/dist/exporters/matrix/export.d.ts +9 -1
  81. package/dist/exporters/matrix/export.d.ts.map +1 -1
  82. package/dist/exporters/matrix/export.js +21 -3
  83. package/dist/exporters/matrix/export.js.map +1 -1
  84. package/dist/exporters/matrix/gates.js +6 -0
  85. package/dist/exporters/matrix/gates.js.map +1 -1
  86. package/dist/exporters/matrix/render-csv.js +1 -1
  87. package/dist/exporters/matrix/render-csv.js.map +1 -1
  88. package/dist/exporters/matrix/render-testcase-sheet.d.ts.map +1 -1
  89. package/dist/exporters/matrix/render-testcase-sheet.js +10 -4
  90. package/dist/exporters/matrix/render-testcase-sheet.js.map +1 -1
  91. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  92. package/dist/exporters/matrix/render-xlsx.js +3 -0
  93. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  94. package/dist/exporters/matrix/types.d.ts +13 -1
  95. package/dist/exporters/matrix/types.d.ts.map +1 -1
  96. package/dist/exporters/matrix/types.js.map +1 -1
  97. package/dist/exporters/matrix/wording.d.ts +8 -1
  98. package/dist/exporters/matrix/wording.d.ts.map +1 -1
  99. package/dist/exporters/matrix/wording.js +37 -2
  100. package/dist/exporters/matrix/wording.js.map +1 -1
  101. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  102. package/dist/exporters/playwright-report-parser.js +1 -0
  103. package/dist/exporters/playwright-report-parser.js.map +1 -1
  104. package/dist/exporters/run-environment.d.ts +32 -0
  105. package/dist/exporters/run-environment.d.ts.map +1 -0
  106. package/dist/exporters/run-environment.js +125 -0
  107. package/dist/exporters/run-environment.js.map +1 -0
  108. package/dist/exporters/scenario-merger.d.ts +18 -0
  109. package/dist/exporters/scenario-merger.d.ts.map +1 -1
  110. package/dist/exporters/scenario-merger.js +49 -3
  111. package/dist/exporters/scenario-merger.js.map +1 -1
  112. package/dist/exporters/test-data-resolver.d.ts +0 -4
  113. package/dist/exporters/test-data-resolver.d.ts.map +1 -1
  114. package/dist/exporters/test-data-resolver.js +0 -7
  115. package/dist/exporters/test-data-resolver.js.map +1 -1
  116. package/dist/exporters/types.d.ts +5 -0
  117. package/dist/exporters/types.d.ts.map +1 -1
  118. package/dist/exporters/xlsx-exporter.d.ts +2 -0
  119. package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
  120. package/dist/exporters/xlsx-exporter.js +7 -2
  121. package/dist/exporters/xlsx-exporter.js.map +1 -1
  122. package/dist/generators/gherkin-parser/index.d.ts +11 -4
  123. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  124. package/dist/generators/gherkin-parser/index.js +49 -19
  125. package/dist/generators/gherkin-parser/index.js.map +1 -1
  126. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +4 -0
  127. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  128. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
  129. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
  130. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
  131. package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +68 -4
  132. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +1 -1
  133. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -1
  134. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +7 -1
  135. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
  136. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
  137. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +5 -1
  138. package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
  139. package/dist/generators/test-generator/code-generator.d.ts +2 -1
  140. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  141. package/dist/generators/test-generator/code-generator.js +90 -5
  142. package/dist/generators/test-generator/code-generator.js.map +1 -1
  143. package/dist/generators/test-generator/diagnostics.d.ts +36 -5
  144. package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
  145. package/dist/generators/test-generator/diagnostics.js +35 -4
  146. package/dist/generators/test-generator/diagnostics.js.map +1 -1
  147. package/dist/generators/test-generator/patterns/index.d.ts +12 -2
  148. package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
  149. package/dist/generators/test-generator/patterns/index.js +15 -9
  150. package/dist/generators/test-generator/patterns/index.js.map +1 -1
  151. package/dist/generators/test-generator/patterns/types.d.ts +15 -0
  152. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  153. package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +5 -0
  154. package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -1
  155. package/dist/generators/test-generator/patterns/with-parent-scope.js +6 -0
  156. package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -1
  157. package/dist/generators/test-generator/step-mapper.d.ts +46 -0
  158. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  159. package/dist/generators/test-generator/step-mapper.js +84 -9
  160. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  161. package/dist/generators/test-generator/utils/data-resolver.d.ts +25 -0
  162. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  163. package/dist/generators/test-generator/utils/data-resolver.js +39 -8
  164. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  165. package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -1
  166. package/dist/generators/test-generator/utils/selector-entry-fields.js +1 -0
  167. package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -1
  168. package/dist/generators/test-generator/utils/selector-resolver.d.ts +37 -4
  169. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  170. package/dist/generators/test-generator/utils/selector-resolver.js +212 -79
  171. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  172. package/dist/harness/audit.d.ts +3 -0
  173. package/dist/harness/audit.d.ts.map +1 -1
  174. package/dist/harness/audit.js +60 -19
  175. package/dist/harness/audit.js.map +1 -1
  176. package/dist/harness/capability.d.ts +7 -1
  177. package/dist/harness/capability.d.ts.map +1 -1
  178. package/dist/harness/capability.js +15 -12
  179. package/dist/harness/capability.js.map +1 -1
  180. package/dist/harness/catalog/universal-viewpoints.yaml +5 -5
  181. package/dist/harness/config-file.d.ts +17 -0
  182. package/dist/harness/config-file.d.ts.map +1 -0
  183. package/dist/harness/config-file.js +100 -0
  184. package/dist/harness/config-file.js.map +1 -0
  185. package/dist/harness/counted-assertions.d.ts +2 -0
  186. package/dist/harness/counted-assertions.d.ts.map +1 -0
  187. package/dist/harness/counted-assertions.js +38 -0
  188. package/dist/harness/counted-assertions.js.map +1 -0
  189. package/dist/harness/data-driven-lint.d.ts.map +1 -1
  190. package/dist/harness/data-driven-lint.js +69 -27
  191. package/dist/harness/data-driven-lint.js.map +1 -1
  192. package/dist/harness/defect-draft.d.ts +14 -0
  193. package/dist/harness/defect-draft.d.ts.map +1 -0
  194. package/dist/harness/defect-draft.js +143 -0
  195. package/dist/harness/defect-draft.js.map +1 -0
  196. package/dist/harness/defects.d.ts +24 -0
  197. package/dist/harness/defects.d.ts.map +1 -0
  198. package/dist/harness/defects.js +118 -0
  199. package/dist/harness/defects.js.map +1 -0
  200. package/dist/harness/depth-lint.d.ts.map +1 -1
  201. package/dist/harness/depth-lint.js +2 -1
  202. package/dist/harness/depth-lint.js.map +1 -1
  203. package/dist/harness/flow-check.d.ts.map +1 -1
  204. package/dist/harness/flow-check.js +6 -2
  205. package/dist/harness/flow-check.js.map +1 -1
  206. package/dist/harness/keyword-match.d.ts +59 -0
  207. package/dist/harness/keyword-match.d.ts.map +1 -0
  208. package/dist/harness/keyword-match.js +86 -0
  209. package/dist/harness/keyword-match.js.map +1 -0
  210. package/dist/harness/manifest.d.ts.map +1 -1
  211. package/dist/harness/manifest.js +2 -2
  212. package/dist/harness/manifest.js.map +1 -1
  213. package/dist/harness/next-step.d.ts +2 -4
  214. package/dist/harness/next-step.d.ts.map +1 -1
  215. package/dist/harness/next-step.js +50 -5
  216. package/dist/harness/next-step.js.map +1 -1
  217. package/dist/harness/parse.d.ts +45 -0
  218. package/dist/harness/parse.d.ts.map +1 -1
  219. package/dist/harness/parse.js +142 -24
  220. package/dist/harness/parse.js.map +1 -1
  221. package/dist/harness/perf.d.ts +7 -1
  222. package/dist/harness/perf.d.ts.map +1 -1
  223. package/dist/harness/perf.js +13 -10
  224. package/dist/harness/perf.js.map +1 -1
  225. package/dist/harness/quality-gates.d.ts +9 -2
  226. package/dist/harness/quality-gates.d.ts.map +1 -1
  227. package/dist/harness/quality-gates.js +22 -8
  228. package/dist/harness/quality-gates.js.map +1 -1
  229. package/dist/harness/query-catalog.js +0 -0
  230. package/dist/harness/query-catalog.js.map +1 -1
  231. package/dist/harness/read-text.d.ts +21 -0
  232. package/dist/harness/read-text.d.ts.map +1 -1
  233. package/dist/harness/read-text.js +32 -1
  234. package/dist/harness/read-text.js.map +1 -1
  235. package/dist/harness/script-check.d.ts.map +1 -1
  236. package/dist/harness/script-check.js +7 -1
  237. package/dist/harness/script-check.js.map +1 -1
  238. package/dist/harness/secret-scan.d.ts +27 -1
  239. package/dist/harness/secret-scan.d.ts.map +1 -1
  240. package/dist/harness/secret-scan.js +57 -4
  241. package/dist/harness/secret-scan.js.map +1 -1
  242. package/dist/harness/select-by-ids.d.ts +31 -0
  243. package/dist/harness/select-by-ids.d.ts.map +1 -0
  244. package/dist/harness/select-by-ids.js +106 -0
  245. package/dist/harness/select-by-ids.js.map +1 -0
  246. package/dist/harness/sensors.d.ts +20 -1
  247. package/dist/harness/sensors.d.ts.map +1 -1
  248. package/dist/harness/sensors.js +133 -39
  249. package/dist/harness/sensors.js.map +1 -1
  250. package/dist/harness/spec-branches.d.ts +0 -18
  251. package/dist/harness/spec-branches.d.ts.map +1 -1
  252. package/dist/harness/spec-branches.js +24 -3
  253. package/dist/harness/spec-branches.js.map +1 -1
  254. package/dist/harness/spec-coverage.d.ts.map +1 -1
  255. package/dist/harness/spec-coverage.js +14 -4
  256. package/dist/harness/spec-coverage.js.map +1 -1
  257. package/dist/harness/triage-signals.d.ts +20 -0
  258. package/dist/harness/triage-signals.d.ts.map +1 -0
  259. package/dist/harness/triage-signals.js +30 -0
  260. package/dist/harness/triage-signals.js.map +1 -0
  261. package/dist/harness/triage-unit.d.ts +23 -0
  262. package/dist/harness/triage-unit.d.ts.map +1 -0
  263. package/dist/harness/triage-unit.js +142 -0
  264. package/dist/harness/triage-unit.js.map +1 -0
  265. package/dist/harness/triage.d.ts +28 -0
  266. package/dist/harness/triage.d.ts.map +1 -0
  267. package/dist/harness/triage.js +99 -0
  268. package/dist/harness/triage.js.map +1 -0
  269. package/dist/harness/ui-inventory.d.ts +40 -0
  270. package/dist/harness/ui-inventory.d.ts.map +1 -0
  271. package/dist/harness/ui-inventory.js +191 -0
  272. package/dist/harness/ui-inventory.js.map +1 -0
  273. package/dist/harness/viewpoint-baseline.d.ts.map +1 -1
  274. package/dist/harness/viewpoint-baseline.js +7 -1
  275. package/dist/harness/viewpoint-baseline.js.map +1 -1
  276. package/dist/harness/viewpoint-draft-header.d.ts +7 -0
  277. package/dist/harness/viewpoint-draft-header.d.ts.map +1 -0
  278. package/dist/harness/viewpoint-draft-header.js +100 -0
  279. package/dist/harness/viewpoint-draft-header.js.map +1 -0
  280. package/dist/harness/viewpoint-ledger.d.ts +13 -3
  281. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  282. package/dist/harness/viewpoint-ledger.js +106 -19
  283. package/dist/harness/viewpoint-ledger.js.map +1 -1
  284. package/dist/harness/viewpoint-mode.d.ts +9 -0
  285. package/dist/harness/viewpoint-mode.d.ts.map +1 -0
  286. package/dist/harness/viewpoint-mode.js +52 -0
  287. package/dist/harness/viewpoint-mode.js.map +1 -0
  288. package/dist/harness/viewpoint-priority.d.ts +19 -0
  289. package/dist/harness/viewpoint-priority.d.ts.map +1 -0
  290. package/dist/harness/viewpoint-priority.js +36 -0
  291. package/dist/harness/viewpoint-priority.js.map +1 -0
  292. package/dist/harness/viewpoint-review-open.d.ts +6 -0
  293. package/dist/harness/viewpoint-review-open.d.ts.map +1 -0
  294. package/dist/harness/viewpoint-review-open.js +90 -0
  295. package/dist/harness/viewpoint-review-open.js.map +1 -0
  296. package/dist/harness/viewpoint-scan-refs.d.ts +50 -0
  297. package/dist/harness/viewpoint-scan-refs.d.ts.map +1 -0
  298. package/dist/harness/viewpoint-scan-refs.js +200 -0
  299. package/dist/harness/viewpoint-scan-refs.js.map +1 -0
  300. package/dist/harness/viewpoint-scan.d.ts +41 -0
  301. package/dist/harness/viewpoint-scan.d.ts.map +1 -0
  302. package/dist/harness/viewpoint-scan.js +118 -0
  303. package/dist/harness/viewpoint-scan.js.map +1 -0
  304. package/dist/harness/write-atomic.d.ts +2 -0
  305. package/dist/harness/write-atomic.d.ts.map +1 -0
  306. package/dist/harness/write-atomic.js +62 -0
  307. package/dist/harness/write-atomic.js.map +1 -0
  308. package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -1
  309. package/dist/orchestrator/ai-skills/writer.js +15 -7
  310. package/dist/orchestrator/ai-skills/writer.js.map +1 -1
  311. package/dist/orchestrator/assets-drift.d.ts.map +1 -1
  312. package/dist/orchestrator/assets-drift.js +11 -6
  313. package/dist/orchestrator/assets-drift.js.map +1 -1
  314. package/dist/orchestrator/flow-manager.d.ts +0 -1
  315. package/dist/orchestrator/flow-manager.d.ts.map +1 -1
  316. package/dist/orchestrator/flow-manager.js +3 -52
  317. package/dist/orchestrator/flow-manager.js.map +1 -1
  318. package/dist/orchestrator/project-deps-sync.d.ts.map +1 -1
  319. package/dist/orchestrator/project-deps-sync.js +22 -1
  320. package/dist/orchestrator/project-deps-sync.js.map +1 -1
  321. package/dist/orchestrator/project-initializer.d.ts +8 -0
  322. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  323. package/dist/orchestrator/project-initializer.js +49 -7
  324. package/dist/orchestrator/project-initializer.js.map +1 -1
  325. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  326. package/dist/orchestrator/screen-manager.js +8 -62
  327. package/dist/orchestrator/screen-manager.js.map +1 -1
  328. package/dist/orchestrator/security-overrides-catalog.d.ts +39 -0
  329. package/dist/orchestrator/security-overrides-catalog.d.ts.map +1 -0
  330. package/dist/orchestrator/security-overrides-catalog.js +63 -0
  331. package/dist/orchestrator/security-overrides-catalog.js.map +1 -0
  332. package/dist/orchestrator/security-overrides.d.ts +51 -0
  333. package/dist/orchestrator/security-overrides.d.ts.map +1 -0
  334. package/dist/orchestrator/security-overrides.js +165 -0
  335. package/dist/orchestrator/security-overrides.js.map +1 -0
  336. package/dist/orchestrator/templates/ai-src/agents/discovery.md +11 -3
  337. package/dist/orchestrator/templates/ai-src/agents/reviewer.md +8 -0
  338. package/dist/orchestrator/templates/ai-src/commands/create-test.md +32 -24
  339. package/dist/orchestrator/templates/ai-src/commands/review.md +2 -2
  340. package/dist/orchestrator/templates/ai-src/commands/run-test.md +78 -15
  341. package/dist/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
  342. package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
  343. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +25 -1
  344. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +26 -8
  345. package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +5 -1
  346. package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
  347. package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
  348. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +42 -2
  349. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
  350. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +18 -0
  351. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +63 -0
  352. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +158 -0
  353. package/dist/orchestrator/templates/playwright.config.ts +4 -2
  354. package/dist/orchestrator/templates/specs-locale-fixture.ts +21 -9
  355. package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
  356. package/dist/orchestrator/templates/specs-url-assert.d.ts +17 -1
  357. package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
  358. package/dist/orchestrator/templates/specs-url-assert.js +38 -4
  359. package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
  360. package/dist/orchestrator/templates/specs-url-assert.ts +47 -4
  361. package/dist/orchestrator/templates/wdio.conf.ts +24 -1
  362. package/dist/orchestrator/trace-mode-install.d.ts +14 -0
  363. package/dist/orchestrator/trace-mode-install.d.ts.map +1 -0
  364. package/dist/orchestrator/trace-mode-install.js +132 -0
  365. package/dist/orchestrator/trace-mode-install.js.map +1 -0
  366. package/dist/orchestrator/viewpoint-pattern-catalog.d.ts +23 -0
  367. package/dist/orchestrator/viewpoint-pattern-catalog.d.ts.map +1 -0
  368. package/dist/orchestrator/viewpoint-pattern-catalog.js +48 -0
  369. package/dist/orchestrator/viewpoint-pattern-catalog.js.map +1 -0
  370. package/dist/orchestrator/viewpoint-scaffold.d.ts +3 -0
  371. package/dist/orchestrator/viewpoint-scaffold.d.ts.map +1 -0
  372. package/dist/orchestrator/viewpoint-scaffold.js +70 -0
  373. package/dist/orchestrator/viewpoint-scaffold.js.map +1 -0
  374. package/package.json +3 -3
  375. package/src/cli/commands/audit.ts +59 -12
  376. package/src/cli/commands/capability.ts +54 -19
  377. package/src/cli/commands/changed.ts +2 -4
  378. package/src/cli/commands/dashboard.ts +1 -26
  379. package/src/cli/commands/defect.ts +49 -0
  380. package/src/cli/commands/delivery.ts +106 -56
  381. package/src/cli/commands/generate.ts +15 -7
  382. package/src/cli/commands/results-merge.ts +9 -9
  383. package/src/cli/commands/results-sanitize.ts +2 -9
  384. package/src/cli/commands/select.ts +88 -0
  385. package/src/cli/commands/triage.ts +57 -0
  386. package/src/cli/commands/update.ts +3 -0
  387. package/src/cli/commands/viewpoint.ts +58 -0
  388. package/src/cli/index.ts +13 -2
  389. package/src/dashboard/snapshot-builder.ts +6 -8
  390. package/src/dashboard/templates/index.html +1 -1
  391. package/src/dashboard/types.ts +1 -0
  392. package/src/exporters/csv-exporter.ts +12 -11
  393. package/src/exporters/feature-parser.ts +44 -2
  394. package/src/exporters/json-exporter.ts +6 -1
  395. package/src/exporters/locale-variants.ts +23 -17
  396. package/src/exporters/manual-hybrid-oracle.ts +112 -0
  397. package/src/exporters/matrix/build.ts +71 -19
  398. package/src/exporters/matrix/carry-manual-results.ts +292 -0
  399. package/src/exporters/matrix/export.ts +24 -4
  400. package/src/exporters/matrix/gates.ts +6 -0
  401. package/src/exporters/matrix/render-csv.ts +1 -1
  402. package/src/exporters/matrix/render-testcase-sheet.ts +10 -4
  403. package/src/exporters/matrix/render-xlsx.ts +3 -0
  404. package/src/exporters/matrix/types.ts +13 -1
  405. package/src/exporters/matrix/wording.ts +36 -2
  406. package/src/exporters/playwright-report-parser.ts +1 -0
  407. package/src/exporters/run-environment.ts +89 -0
  408. package/src/exporters/scenario-merger.ts +51 -5
  409. package/src/exporters/test-data-resolver.ts +0 -6
  410. package/src/exporters/types.ts +5 -0
  411. package/src/exporters/xlsx-exporter.ts +7 -2
  412. package/src/generators/gherkin-parser/index.ts +50 -18
  413. package/src/generators/test-generator/adapters/adapter-interface.ts +6 -2
  414. package/src/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
  415. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
  416. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
  417. package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +68 -4
  418. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +1 -1
  419. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -1
  420. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +7 -1
  421. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
  422. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
  423. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +5 -1
  424. package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
  425. package/src/generators/test-generator/code-generator.ts +77 -5
  426. package/src/generators/test-generator/diagnostics.ts +36 -5
  427. package/src/generators/test-generator/patterns/index.ts +15 -8
  428. package/src/generators/test-generator/patterns/types.ts +15 -0
  429. package/src/generators/test-generator/patterns/with-parent-scope.ts +6 -0
  430. package/src/generators/test-generator/step-mapper.ts +90 -8
  431. package/src/generators/test-generator/utils/data-resolver.ts +41 -12
  432. package/src/generators/test-generator/utils/selector-entry-fields.ts +1 -0
  433. package/src/generators/test-generator/utils/selector-resolver.ts +238 -82
  434. package/src/harness/audit.ts +69 -24
  435. package/src/harness/capability.ts +23 -12
  436. package/src/harness/catalog/universal-viewpoints.yaml +5 -5
  437. package/src/harness/config-file.ts +66 -0
  438. package/src/harness/counted-assertions.ts +33 -0
  439. package/src/harness/data-driven-lint.ts +82 -24
  440. package/src/harness/defect-draft.ts +115 -0
  441. package/src/harness/defects.ts +94 -0
  442. package/src/harness/depth-lint.ts +2 -1
  443. package/src/harness/flow-check.ts +6 -2
  444. package/src/harness/keyword-match.ts +85 -0
  445. package/src/harness/manifest.ts +2 -2
  446. package/src/harness/next-step.ts +47 -5
  447. package/src/harness/parse.ts +151 -27
  448. package/src/harness/perf.ts +15 -7
  449. package/src/harness/quality-gates.ts +22 -8
  450. package/src/harness/query-catalog.ts +0 -0
  451. package/src/harness/read-text.ts +32 -1
  452. package/src/harness/script-check.ts +7 -1
  453. package/src/harness/secret-scan.ts +60 -4
  454. package/src/harness/select-by-ids.ts +83 -0
  455. package/src/harness/sensors.ts +141 -40
  456. package/src/harness/spec-branches.ts +6 -3
  457. package/src/harness/spec-coverage.ts +14 -4
  458. package/src/harness/triage-signals.ts +46 -0
  459. package/src/harness/triage-unit.ts +113 -0
  460. package/src/harness/triage.ts +127 -0
  461. package/src/harness/ui-inventory.ts +164 -0
  462. package/src/harness/viewpoint-baseline.ts +7 -1
  463. package/src/harness/viewpoint-draft-header.ts +55 -0
  464. package/src/harness/viewpoint-ledger.ts +113 -25
  465. package/src/harness/viewpoint-mode.ts +49 -0
  466. package/src/harness/viewpoint-priority.ts +38 -0
  467. package/src/harness/viewpoint-review-open.ts +51 -0
  468. package/src/harness/viewpoint-scan-refs.ts +154 -0
  469. package/src/harness/viewpoint-scan.ts +111 -0
  470. package/src/harness/write-atomic.ts +22 -0
  471. package/src/orchestrator/ai-skills/writer.ts +18 -3
  472. package/src/orchestrator/assets-drift.ts +10 -5
  473. package/src/orchestrator/flow-manager.ts +3 -53
  474. package/src/orchestrator/project-deps-sync.ts +23 -1
  475. package/src/orchestrator/project-initializer.ts +54 -6
  476. package/src/orchestrator/screen-manager.ts +7 -61
  477. package/src/orchestrator/security-overrides-catalog.ts +69 -0
  478. package/src/orchestrator/security-overrides.ts +149 -0
  479. package/src/orchestrator/templates/ai-src/agents/discovery.md +11 -3
  480. package/src/orchestrator/templates/ai-src/agents/reviewer.md +8 -0
  481. package/src/orchestrator/templates/ai-src/commands/create-test.md +32 -24
  482. package/src/orchestrator/templates/ai-src/commands/review.md +2 -2
  483. package/src/orchestrator/templates/ai-src/commands/run-test.md +78 -15
  484. package/src/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
  485. package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
  486. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +25 -1
  487. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +26 -8
  488. package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +5 -1
  489. package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
  490. package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
  491. package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +42 -2
  492. package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
  493. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +18 -0
  494. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +63 -0
  495. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +158 -0
  496. package/src/orchestrator/templates/playwright.config.ts +4 -2
  497. package/src/orchestrator/templates/specs-locale-fixture.ts +21 -9
  498. package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
  499. package/src/orchestrator/templates/specs-url-assert.ts +47 -4
  500. package/src/orchestrator/templates/wdio.conf.ts +24 -1
  501. package/src/orchestrator/trace-mode-install.ts +94 -0
  502. package/src/orchestrator/viewpoint-pattern-catalog.ts +61 -0
  503. package/src/orchestrator/viewpoint-scaffold.ts +36 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * `sungen defect draft` — turn each `product-candidate` row of a unit's last triage into a record
3
+ * `qa/defects/DEF-NNN-<slug>.md` (see defects.ts for the link checks). Deterministic: Steps come
4
+ * from the scenario's Gherkin with test-data resolved, Expected/Actual from the assertion, and the
5
+ * severity is PROPOSED by rule until QA confirms it. Never overwrites a file, and never drafts a
6
+ * scenario an existing record already names.
7
+ */
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import { stringify as toYaml } from 'yaml';
11
+ import type { ResolvedUnit } from '../cli/resolve-unit';
12
+ import { redactSecrets } from '../exporters/api-testcase-formatter';
13
+ import { getEnvironment } from '../exporters/run-environment';
14
+ import { parseFeatureMetadata } from '../exporters/feature-parser';
15
+ import { mergeFeatureAndSpec, MergedScenario } from '../exporters/scenario-merger';
16
+ import { maskCredentialValue } from '../exporters/sensitive-key';
17
+ import { parseSpecFile } from '../exporters/spec-parser';
18
+ import { loadTestData, substituteTestDataVars } from '../exporters/test-data-resolver';
19
+ import { readCapabilities } from './capability';
20
+ import { defectsDir, nextDefectId, readDefects } from './defects';
21
+ import type { TriageRow } from './triage';
22
+ import { readTriageReport, unitSecrets } from './triage-unit';
23
+
24
+ export interface DraftResult {
25
+ /** `sungen triage` has not run for this unit. */
26
+ missingTriage?: boolean;
27
+ written: string[];
28
+ skipped: Array<{ scenario: string; id: string }>;
29
+ }
30
+
31
+ /** Blocking actions: a @high scenario that fails on one of these is `major`, otherwise `minor`. */
32
+ const BLOCKING = /\b(?:submit|save|apply|log ?in|sign ?in|sign ?up|register|checkout|pay|confirm|send|navigate|redirect)\b/i;
33
+
34
+ const slug = (s: string): string =>
35
+ s.replace(/^\s*(?:VP|FL)-[A-Z0-9-]+\s+/i, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 50).replace(/-$/, '');
36
+
37
+ interface UnitScenario { merged: MergedScenario; testData: Record<string, string>; secrets: string[] }
38
+
39
+ /** Scenario title → its merged Gherkin, test-data (credentials masked) and secret values. */
40
+ function unitScenarios(cwd: string, unit: ResolvedUnit): Map<string, UnitScenario> {
41
+ const out = new Map<string, UnitScenario>();
42
+ const featuresDir = path.join(unit.dir, 'features');
43
+ if (!fs.existsSync(featuresDir)) return out;
44
+ for (const f of fs.readdirSync(featuresDir).filter((x) => x.endsWith('.feature'))) {
45
+ const base = f.replace(/\.feature$/, '');
46
+ const spec = parseSpecFile(path.join(cwd, 'specs', 'generated', unit.unitId, `${base}.spec.ts`));
47
+ const dataFile = path.join(unit.dir, 'test-data', `${base}.yaml`);
48
+ const raw = fs.existsSync(dataFile) ? loadTestData(dataFile) : {};
49
+ const testData = Object.fromEntries(Object.entries(raw).map(([k, v]) => [k, maskCredentialValue(k, String(v))]));
50
+ const secrets = unitSecrets(raw, cwd);
51
+ for (const m of mergeFeatureAndSpec(parseFeatureMetadata(path.join(featuresDir, f)), spec)) {
52
+ out.set(m.feature.name, { merged: m, testData, secrets });
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+
58
+ function render(cwd: string, unit: ResolvedUnit, id: string, row: TriageRow, sc: UnitScenario | undefined): string {
59
+ const tags = sc?.merged.feature.tags ?? [];
60
+ const resolve = (t: string): string => (sc ? substituteTestDataVars(t, sc.testData) : t);
61
+ const steps = (sc?.merged.resolvedSteps ?? []).map((s) => resolve(s.text));
62
+ const thens = (sc?.merged.resolvedExpected ?? []).map((s) => resolve(s.text));
63
+ const high = tags.some((t) => t === '@high' || t === '@critical');
64
+ const severity = high && BLOCKING.test([row.scenario, ...steps].join(' ')) ? 'major' : 'minor';
65
+ const specFile = path.join(unit.dir, 'requirements', 'spec.md');
66
+ const env = getEnvironment(cwd);
67
+ const front = {
68
+ id, title: row.scenario, severity, severity_source: 'proposed', status: 'open',
69
+ units: [unit.unitId], scenarios: [row.scenario], claims: row.ids, legacy: row.legacy,
70
+ spec: fs.existsSync(specFile) ? path.relative(cwd, specFile) : '',
71
+ found: {
72
+ date: new Date().toISOString().slice(0, 10),
73
+ env: process.env.SUNGEN_ENV || 'default',
74
+ base_url: env.baseURL,
75
+ platform: row.variant || readCapabilities(cwd).platform || 'web',
76
+ },
77
+ tracker: '',
78
+ };
79
+ const body = [
80
+ '---', toYaml(front).trimEnd(), '---', '',
81
+ `# ${id}: ${row.scenario}`, '',
82
+ '## Summary', '', '(to fill: what is wrong, where, and who it affects)', '',
83
+ '## Steps to reproduce', '', ...(steps.length ? steps.map((s, i) => `${i + 1}. ${s}`) : ['(scenario steps not found — see the feature)']), '',
84
+ '## Expected', '', ...thens.map((t) => `- ${t}`), ...(row.expected ? [`- Asserted value: ${row.expected}`] : []), '',
85
+ '## Actual', '', `- ${row.received ? `Received: ${row.received}` : row.evidence}`, '',
86
+ '## Evidence', '', `- Error: ${row.evidence}`, ...(row.tracePath ? [`- Trace: ${row.tracePath}`] : []), '',
87
+ '## Notes', '', `- Triage: ${row.class} (${row.ruleId})${row.variant ? ` on ${row.variant}` : ''}`, '',
88
+ ].join('\n');
89
+ return redactSecrets(body, sc?.secrets ?? []);
90
+ }
91
+
92
+ export function draftDefects(cwd: string, unit: ResolvedUnit, opts: { scenario?: string } = {}): DraftResult {
93
+ const triage = readTriageReport(cwd, unit.unitId);
94
+ if (!triage) return { missingTriage: true, written: [], skipped: [] };
95
+ const recorded = new Map<string, string>();
96
+ for (const r of readDefects(cwd)) if (r.units.includes(unit.unitId)) for (const s of r.scenarios) recorded.set(s, r.id);
97
+
98
+ const wanted = triage.rows.filter((r) => r.class === 'product-candidate'
99
+ && (!opts.scenario || r.scenario.includes(opts.scenario) || r.ids.includes(opts.scenario)));
100
+ const scenarios = unitScenarios(cwd, unit);
101
+ const result: DraftResult = { written: [], skipped: [] };
102
+ const seen = new Set<string>();
103
+ for (const row of wanted) {
104
+ if (seen.has(row.scenario)) continue; // one record per scenario, even across OS variants
105
+ seen.add(row.scenario);
106
+ const existing = recorded.get(row.scenario);
107
+ if (existing) { result.skipped.push({ scenario: row.scenario, id: existing }); continue; }
108
+ const id = nextDefectId(cwd);
109
+ const file = path.join(defectsDir(cwd), `${id}-${slug(row.scenario) || 'defect'}.md`);
110
+ fs.mkdirSync(path.dirname(file), { recursive: true });
111
+ fs.writeFileSync(file, render(cwd, unit, id, row, scenarios.get(row.scenario)), { encoding: 'utf-8', flag: 'wx' });
112
+ result.written.push(path.relative(cwd, file));
113
+ }
114
+ return result;
115
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Defect records — `qa/defects/DEF-NNN-<slug>.md`, one project-wide folder. A scenario points at a
3
+ * record with `@known-defect:DEF-NNN`; the record points back at units, scenarios, claims and legacy
4
+ * TCs in its frontmatter. This module reads the records and checks both directions of that link.
5
+ * Drafting a record from a triage row lives in defect-draft.ts.
6
+ */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+ import { parse as parseYaml } from 'yaml';
10
+
11
+ export const DEFECT_STATUSES = ['open', 'fixed', 'verified', 'closed', 'wont-fix'] as const;
12
+ export type DefectStatus = typeof DEFECT_STATUSES[number];
13
+
14
+ export interface DefectRecord {
15
+ id: string;
16
+ title: string;
17
+ severity: string;
18
+ status: string;
19
+ units: string[];
20
+ scenarios: string[];
21
+ /** Project-relative path of the record file. */
22
+ file: string;
23
+ }
24
+
25
+ const DEF_FILE = /^(DEF-(\d+))-.*\.md$/;
26
+ const KNOWN_DEFECT_TAG = /@known-defect:(DEF-\d+)\b/g;
27
+
28
+ export function defectsDir(cwd: string): string {
29
+ return path.join(cwd, 'qa', 'defects');
30
+ }
31
+
32
+ function defectFiles(cwd: string): string[] {
33
+ const dir = defectsDir(cwd);
34
+ return fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => DEF_FILE.test(f)).sort() : [];
35
+ }
36
+
37
+ /** The next free id: one above the highest number on disk, zero-padded to 3. */
38
+ export function nextDefectId(cwd: string): string {
39
+ const max = defectFiles(cwd).reduce((m, f) => Math.max(m, Number(f.match(DEF_FILE)![2])), 0);
40
+ return `DEF-${String(max + 1).padStart(3, '0')}`;
41
+ }
42
+
43
+ const list = (v: unknown): string[] => (Array.isArray(v) ? v.map(String) : []);
44
+
45
+ /** Every record whose frontmatter parses; a malformed file is skipped, never thrown on. */
46
+ export function readDefects(cwd: string): DefectRecord[] {
47
+ const out: DefectRecord[] = [];
48
+ for (const f of defectFiles(cwd)) {
49
+ const file = path.join(defectsDir(cwd), f);
50
+ try {
51
+ const fm = parseYaml(fs.readFileSync(file, 'utf-8').split(/^---\s*$/m)[1] ?? '') as Record<string, unknown> | null;
52
+ if (!fm || typeof fm !== 'object') continue;
53
+ out.push({
54
+ id: String(fm.id ?? f.match(DEF_FILE)![1]),
55
+ title: String(fm.title ?? ''),
56
+ severity: String(fm.severity ?? ''),
57
+ status: String(fm.status ?? ''),
58
+ units: list(fm.units),
59
+ scenarios: list(fm.scenarios),
60
+ file: path.relative(cwd, file),
61
+ });
62
+ } catch {
63
+ // unreadable frontmatter: not a record we can reason about
64
+ }
65
+ }
66
+ return out;
67
+ }
68
+
69
+ /**
70
+ * Advisory audit findings for one unit (never scored):
71
+ * DEFECT-REF-MISSING a `@known-defect:DEF-NNN` tag with no record file.
72
+ * DEFECT-ORPHAN (info) an OPEN record of this unit names a scenario the unit no longer has.
73
+ */
74
+ export function defectLinkFindings(cwd: string, unitId: string, featureText: string, scenarioNames: string[]): string[] {
75
+ const records = readDefects(cwd);
76
+ const findings: string[] = [];
77
+ const known = new Set(records.map((r) => r.id));
78
+ const tagged = new Set([...featureText.matchAll(KNOWN_DEFECT_TAG)].map((m) => m[1]));
79
+ for (const id of tagged) {
80
+ if (!known.has(id)) {
81
+ findings.push(`DEFECT-REF-MISSING: \`@known-defect:${id}\` has no record in qa/defects/ — draft it with \`sungen defect draft --screen ${unitId}\`, or correct the id.`);
82
+ }
83
+ }
84
+ const names = new Set(scenarioNames);
85
+ for (const r of records) {
86
+ if (r.status !== 'open' || !r.units.includes(unitId)) continue;
87
+ for (const s of r.scenarios) {
88
+ if (!names.has(s)) {
89
+ findings.push(`DEFECT-ORPHAN (info): ${r.id} is open and names scenario "${s}", which ${unitId} no longer has — update the record's scenarios, or close it.`);
90
+ }
91
+ }
92
+ }
93
+ return findings;
94
+ }
@@ -15,6 +15,7 @@ import * as fs from 'fs';
15
15
  import * as path from 'path';
16
16
  import { loadUnitScenarios, parseViewpointOverview, ScenarioInfo, ViewpointEntry } from './parse';
17
17
  import { loadCatalog, viewpointGate, assertionDepth, dataThemesFor, CatalogTheme } from './sensors';
18
+ import { keywordHit } from './keyword-match';
18
19
 
19
20
  export type DepthAction = 'deepen' | 'defer';
20
21
 
@@ -45,7 +46,7 @@ export interface DepthLintReport {
45
46
  function matchTheme(s: ScenarioInfo, dataThemes: CatalogTheme[]): CatalogTheme | undefined {
46
47
  return dataThemes.find((t) => {
47
48
  const kws = t.depth?.keywords?.length ? t.depth.keywords : t.keywords;
48
- return kws.some((k) => s.haystack.includes(k.toLowerCase()));
49
+ return kws.some((k) => keywordHit(s.haystack, k));
49
50
  });
50
51
  }
51
52
 
@@ -12,6 +12,7 @@
12
12
  import * as fs from 'fs';
13
13
  import * as path from 'path';
14
14
  import { featureFilesFor } from './unit-paths';
15
+ import { keywordHit } from './keyword-match';
15
16
  import { loadScenarios } from './parse';
16
17
  import { parseScenarios } from './capability-plan';
17
18
  import { readTextFile } from './read-text';
@@ -45,7 +46,10 @@ function featureTags(fp: string): string[] {
45
46
  }
46
47
  function targetsFromHint(hint: string): string[] {
47
48
  const h = hint.toLowerCase();
48
- const hits = KNOWN_TARGETS.filter((k) => h.includes(k));
49
+ // Word-aware (#779): every KNOWN_TARGET is an ordinary noun, so a substring test read
50
+ // `re·order` / `dis·order` as the target `order` and then matched the two to each other —
51
+ // a deferral about reorder history reported COVERED by a flow scenario about a disorder.
52
+ const hits = KNOWN_TARGETS.filter((k) => keywordHit(h, k));
49
53
  if (hits.length) return hits;
50
54
  // fallback: significant words after "->"
51
55
  const after = h.split('->').pop() || h;
@@ -87,7 +91,7 @@ export function buildFlowCheck(cwd: string, onlyFlow?: string): FlowCheckReport
87
91
  // `@owned-by:<flow>` names the owner explicitly → only that flow's scenarios can cover it
88
92
  // (a false @owned-by is then surfaced as missing). Else any flow may cover it (legacy).
89
93
  const pool = s.ownedBy ? flowScenarios.filter((fs2) => fs2.flow === s.ownedBy) : flowScenarios;
90
- const matches = pool.filter((fs2) => targets.some((t) => fs2.haystack.includes(t)));
94
+ const matches = pool.filter((fs2) => targets.some((t) => keywordHit(fs2.haystack, t)));
91
95
  let verdict: Deferral['verdict'] = 'missing';
92
96
  let via: string | undefined;
93
97
  if (matches.some((m) => m.deep)) { verdict = 'covered'; via = matches.find((m) => m.deep)!.flow; }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Keyword matching for the audit's coverage sensors — the single rule every "does a scenario
3
+ * mention this?" question goes through.
4
+ *
5
+ * WHY THIS EXISTS (#776). The sensors used to ask `haystack.includes(keyword)`. A substring match
6
+ * is satisfied by a keyword buried inside an unrelated word, and it fails in the direction that
7
+ * HIDES problems: a theme nobody tested reads as covered. Measured case — the scenario title
8
+ * "…five steps from email through to information entry" made `format-boundary` covered on a screen
9
+ * with no text input at all, because in·format·ion contains `format`. That one word moved the
10
+ * screen from 7.9 FAIL to 8.9 PASS.
11
+ *
12
+ * THE RULE: anchor a word boundary at the START of the keyword, leave the END free.
13
+ *
14
+ * Not `\b…\b` at both ends, which is the obvious fix and is wrong. Every collision this defect
15
+ * produces is a PREFIX collision — the innocent word carries extra letters BEFORE the keyword:
16
+ *
17
+ * format ← in·format·ion, ·format·ting range ← ar·range·ment, o·range
18
+ * saved ← un·saved created ← re·created
19
+ * success ← un·success·ful length ← wave·length
20
+ *
21
+ * The `unsaved` / `unsuccessful` pair is the sharp end: the prefix INVERTS the meaning, so a
22
+ * scenario asserting a submit FAILED is what marks the submit-SUCCESS theme covered.
23
+ *
24
+ * Meanwhile English inflection lives entirely in the SUFFIX, and a trailing `\b` destroys it:
25
+ * `submit` stops matching "submitted", `success` stops matching "successfully", `valid` stops
26
+ * matching "validation", `create` stops matching "created". Those are true positives, and a
27
+ * both-ends boundary turns 4 of 5 of them into false gaps — trading a silent failure for a noisy
28
+ * one. A leading boundary alone kills all six collisions above and keeps all of them.
29
+ *
30
+ * WHAT THIS RULE DOES NOT FIX, stated rather than implied. A free suffix still admits a collision
31
+ * when the keyword is a true PREFIX of a common unrelated word:
32
+ *
33
+ * feed → "feedback" list → "listener" length → "lengthy"
34
+ *
35
+ * No boundary rule can separate `feed` from `feedback`, so the cure is a less ambiguous KEYWORD,
36
+ * which is a catalog decision rather than a matcher one. #778 took it: the shipped catalog no
37
+ * longer carries any of the three bare forms — `"product list"`, `"feed item"` and
38
+ * `"max length"` / `"min length"` replaced them, and `cart-correctness` lost bare `price` and
39
+ * `quantity` (ordinary listing vocabulary that credited a cart theme on a screen with no cart).
40
+ *
41
+ * That closes today's instances, not the class. `tests/harness/keyword-word-boundary.run.ts`
42
+ * section (e) sweeps every single-word ASCII keyword in the catalog — theme `keywords`,
43
+ * `detect_keywords` and `depth.keywords` alike — so a NEW loose keyword fails the suite instead of
44
+ * entering silently. Adding one is possible, but only as a conscious act via that file's
45
+ * `KNOWN_LOOSE` map, which #778 left empty.
46
+ *
47
+ * NON-ASCII KEYWORDS fall back to substring, on purpose. JavaScript's `\b` is defined against
48
+ * `[A-Za-z0-9_]`, so `\bログイン` and `\bđăng nhập` never match anything at all — the anchor would
49
+ * silently disable the keyword rather than tighten it. Japanese and Chinese have no word
50
+ * boundaries to anchor to, which is precisely where substring matching is the CORRECT behaviour
51
+ * and the false-positive risk is low. Today every keyword reaching here is ASCII by construction
52
+ * (the catalog is English; the ledger and spec sensors extract words with `[a-z]` character
53
+ * classes), so this branch is a guard against a future non-ASCII catalog entry failing closed.
54
+ */
55
+
56
+ /** True when `kw`'s first character can carry a `\b` anchor — i.e. it is an ASCII word character. */
57
+ const anchorable = (kw: string): boolean => /^[a-z0-9_]/i.test(kw);
58
+
59
+ const cache = new Map<string, RegExp | null>();
60
+
61
+ /**
62
+ * Compile `kw` to its matcher, or `null` when the keyword must be compared as a plain substring.
63
+ * Cached: the sensors re-ask the same handful of catalog keywords once per scenario.
64
+ */
65
+ function matcherFor(kw: string): RegExp | null {
66
+ const key = kw.toLowerCase();
67
+ if (cache.has(key)) return cache.get(key)!;
68
+ const re = anchorable(key)
69
+ ? new RegExp(`\\b${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`)
70
+ : null;
71
+ cache.set(key, re);
72
+ return re;
73
+ }
74
+
75
+ /** Does `haystack` mention `kw` as a word (not as the tail of a longer one)? */
76
+ export function keywordHit(haystack: string, kw: string): boolean {
77
+ const h = haystack.toLowerCase();
78
+ const re = matcherFor(kw);
79
+ return re ? re.test(h) : h.includes(kw.toLowerCase());
80
+ }
81
+
82
+ /** Does ANY of `haystacks` mention `kw`? */
83
+ export function hasKeyword(haystacks: string[], kw: string): boolean {
84
+ return haystacks.some((h) => keywordHit(h, kw));
85
+ }
@@ -13,6 +13,7 @@ import * as path from 'path';
13
13
  import { createHash } from 'crypto';
14
14
  import { featureFilesFor, reportSlug } from './unit-paths';
15
15
  import { readTextFile } from './read-text';
16
+ import { vpCodeOf } from './parse';
16
17
 
17
18
  export interface SpecSection { name: string; hash: string }
18
19
  export interface ManifestEntry { scenario: string; vpCode?: string; section: string; specHash: string }
@@ -90,8 +91,7 @@ function parseFeatureSections(featurePath: string): { scenario: string; vpCode?:
90
91
  const s = line.match(/^Scenario:\s*(.*)$/);
91
92
  if (s) {
92
93
  const name = s[1].trim();
93
- const code = name.match(/\bVP-[A-Z]+-\d+/i)?.[0];
94
- out.push({ scenario: name, vpCode: code, section });
94
+ out.push({ scenario: name, vpCode: vpCodeOf(name).vpCode, section });
95
95
  }
96
96
  }
97
97
  return out;
@@ -16,9 +16,12 @@
16
16
  */
17
17
  import * as fs from 'fs';
18
18
  import * as path from 'path';
19
+ import { parse as parseYaml } from 'yaml';
19
20
  import { reportSlug, featureFilesFor } from './unit-paths';
20
21
  import { readTextFile } from './read-text';
21
22
  import { checkAssetsDrift } from '../orchestrator/assets-drift';
23
+ import { viewpointFileIsAiDraft } from './viewpoint-draft-header';
24
+ import { readViewpointDeclaration } from './viewpoint-baseline';
22
25
 
23
26
  export interface NextStep {
24
27
  /** The command to run, as the operator would type it. */
@@ -40,6 +43,8 @@ export interface UnitState {
40
43
  kind: 'screen' | 'flow' | 'api';
41
44
  hasSpec: boolean;
42
45
  hasViewpoint: boolean;
46
+ /** test-viewpoint.md carries `drafted-by: ai` — written by the AI, not yet accepted by a QA. */
47
+ viewpointIsAiDraft?: boolean;
43
48
  hasContract: boolean;
44
49
  hasFlowInventory: boolean;
45
50
  scenarioCount: number;
@@ -87,6 +92,29 @@ function countScenarios(unitDir: string, unit: string): number {
87
92
  * Everything the resolver needs, read from disk. `unitId` is the catalog id
88
93
  * (`<screen>` · `flows/<f>` · `api/<a>`); `unitDir` is its directory.
89
94
  */
95
+ const RESULT_FILE = /-test-result(?:\.[^.]+)*\.json$/;
96
+
97
+ /**
98
+ * Does the selector text resolve at least one element? A scaffold resolves none: the web one holds
99
+ * only the `page` route, the mobile one only header comments. Matching the WORD "placeholder" was
100
+ * wrong both ways (#868): it flagged a real file whose key names an input's placeholder text, and
101
+ * missed both scaffolds, which never contain it.
102
+ */
103
+ function hasResolvedSelector(selectorText: string): boolean {
104
+ let doc: unknown;
105
+ try { doc = parseYaml(selectorText); } catch { return selectorText.trim().length >= 40; }
106
+ if (!doc || typeof doc !== 'object') return false;
107
+ // A real cell, not a marker left to be filled in. Numbers count (an id or index can be numeric).
108
+ const real = (v: unknown): boolean => (typeof v === 'string' || typeof v === 'number')
109
+ && String(v).trim() !== '' && !/^(?:todo|tbd|placeholder|replace me)$/i.test(String(v).trim());
110
+ return Object.values(doc as Record<string, unknown>).some((e) => {
111
+ if (!e || typeof e !== 'object') return false;
112
+ const { type, value, name, locator } = e as { type?: unknown; value?: unknown; name?: unknown; locator?: unknown };
113
+ // Every SelectorEntry shape: a CSS `locator`, or a non-page `type` with a value or a role name.
114
+ return real(locator) || (typeof type === 'string' && type !== 'page' && (real(value) || real(name)));
115
+ });
116
+ }
117
+
90
118
  export function readUnitState(
91
119
  projectRoot: string, unitDir: string, unit: string, kind: UnitState['kind'],
92
120
  ): UnitState {
@@ -101,7 +129,7 @@ export function readUnitState(
101
129
  // `readTextFile` throws on a missing file, and every path here is legitimately absent at some
102
130
  // point in a unit's life — the resolver exists to report exactly that, so it must not crash on it.
103
131
  const read = (p: string): string => (fs.existsSync(p) ? readTextFile(p) : '');
104
- const selectorText = selectorFiles.map((f) => read(path.join(selectorsDir, f))).join('\n');
132
+ const selectorTexts = selectorFiles.map((f) => read(path.join(selectorsDir, f)));
105
133
  const contractPath = path.join(req, 'flow-contract.yaml');
106
134
  const contractText = read(contractPath);
107
135
 
@@ -116,7 +144,9 @@ export function readUnitState(
116
144
  });
117
145
  const hasResults = specDirs.some((d) => {
118
146
  try {
119
- return fs.readdirSync(d).some((f) => f.endsWith('-test-result.json'));
147
+ // `<f>-test-result[.<os>][.<env>].json` — a mobile run writes the OS into the name, an env
148
+ // run (SUNGEN_ENV=staging) the env; both are runs (#868).
149
+ return fs.readdirSync(d).some((f) => RESULT_FILE.test(f));
120
150
  } catch { return false; }
121
151
  });
122
152
  let hasDeliverable = false;
@@ -131,6 +161,9 @@ export function readUnitState(
131
161
  unit, kind,
132
162
  hasSpec: exists(path.join(req, 'spec.md')),
133
163
  hasViewpoint: exists(path.join(req, 'test-viewpoint.md')),
164
+ // An id-less draft has nothing to accept: only the missing-yardstick step speaks for it.
165
+ viewpointIsAiDraft: viewpointFileIsAiDraft(path.join(req, 'test-viewpoint.md'))
166
+ && readViewpointDeclaration(path.join(req, 'test-viewpoint.md')) !== null,
134
167
  hasContract: exists(contractPath),
135
168
  // A contract WITHOUT `flows:` can only be scored per phase, which is a materially weaker
136
169
  // claim — so it is part of the state, not a detail.
@@ -139,8 +172,7 @@ export function readUnitState(
139
172
  hasSelectors: selectorFiles.length > 0,
140
173
  // A scaffolded selector file exists but resolves nothing; treating it as "done" is what
141
174
  // makes a run look further along than it is.
142
- selectorsArePlaceholder: selectorFiles.length > 0
143
- && (/TODO|PLACEHOLDER|REPLACE ME/i.test(selectorText) || selectorText.trim().length < 40),
175
+ selectorsArePlaceholder: selectorFiles.length > 0 && !selectorTexts.some(hasResolvedSelector),
144
176
  compiled, hasResults, hasDeliverable,
145
177
  assetsDrift: (() => {
146
178
  const d = checkAssetsDrift(projectRoot);
@@ -170,6 +202,7 @@ const REPAIRABLE = [
170
202
  'SPEC-RESTATED-UNVERIFIED', 'VIEWPOINT-GESTURE-SUBSTITUTED', 'SPEC-UNCOVERED', 'SPEC-CLAUSE-UNCOVERED',
171
203
  'TRIGGER-UNCOVERED', 'PERMISSION-PAIR-MISSING', 'VISIBILITY-PAIR-MISSING',
172
204
  'VIEWPOINT-ITEM-MISSING', 'MANUAL-CODE-MISSING', 'MANUAL-AUTOMATABLE', 'DEPTH-DEFERRED',
205
+ 'UI-INVENTORY-MISSING', 'UI-INVENTORY-INCOMPLETE', 'UI-ITEM-UNCOVERED',
173
206
  ];
174
207
 
175
208
  /**
@@ -219,6 +252,15 @@ export function deriveSteps(s: UnitState): NextStep[] {
219
252
  });
220
253
  return out;
221
254
  }
255
+ // Before any generation: the AI drafts the viewpoint and audits it BEFORE it writes scenarios, so
256
+ // this is exactly where a draft would otherwise be accepted by the process that wrote it.
257
+ if (s.viewpointIsAiDraft) {
258
+ out.push({
259
+ kind: 'decide',
260
+ command: `sungen audit --screen ${s.unit} --accept-viewpoint`,
261
+ because: 'test-viewpoint.md is marked `drafted-by: ai` — nobody has reviewed it, so the ledger and traceability axes are held as unverified. A QA reviews the claims (adds what matters, deletes what does not), then accepts; the command records the baseline and removes the marker. A decision, not a step: an agent presents it and does not run it',
262
+ });
263
+ }
222
264
  if (s.scenarioCount === 0) {
223
265
  out.push({ kind: 'do', command: design, because: 'no scenarios exist yet' });
224
266
  return out;
@@ -234,7 +276,7 @@ export function deriveSteps(s: UnitState): NextStep[] {
234
276
  out.push({ kind: 'do', command: `sungen audit --screen ${s.unit}`, because: 'the suite has never been measured' });
235
277
  return out;
236
278
  }
237
- if (s.audit.viewpointBaselineStatus === 'changed') {
279
+ if (s.audit.viewpointBaselineStatus === 'changed' && !s.viewpointIsAiDraft) { // the draft's own decide step is already above
238
280
  out.push({
239
281
  kind: 'decide',
240
282
  command: `sungen audit --screen ${s.unit} --accept-viewpoint`,