@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,267 @@
1
+ # Sungen Orchestration & Harness — Detailed Spec
2
+
3
+ > Spec triển khai cho lần refactor lớn: chuyển từ **"Context Engine + Skill" (workflow tuyến tính)** sang **Orchestration + Harness Engine**.
4
+ > Mục tiêu chính: **nâng chất lượng testcase** (đo & gate thay vì nhờ AI). Bất biến: `generate`, auto-fix selector, `delivery`, reports.
5
+ > Tài liệu nền: `reports/sungen_direction_solution.md`, `reports/sungen_refactor_spec.md`, `reports/sungen_home_gherkin_viewpoint_coverage_review.md`.
6
+
7
+ ---
8
+
9
+ ## 0. TL;DR cho người triển khai
10
+
11
+ - **Orchestration** = bộ não điều phối: *khám phá input → chọn route → gọi AI sinh artifact → đo bằng Harness → repair → hội tụ → ghi ledger*.
12
+ - **Harness** = lớp kiểm soát chất lượng: **Guide** (skill markdown, đã có) + **Sensor** (code tất định, MỚI) + **Repair loop** + **QA checkpoint**.
13
+ - **Nguyên tắc vàng:** chất lượng đến từ **đo & gate** (Sensor), không từ "nhờ AI làm đúng" (Guide). Bằng chứng: case `home` 6.5/10 — viewpoint AI tốt nhưng không gì enforce.
14
+ - **Refactor Phase 1 (làm trước, tất định, verify được):** `sungen audit` = 4 Sensor + Viewpoint Gate + score + report. Đây là phần code lõi của lần refactor này.
15
+ - **Orchestrator AI** (`/sungen:design`) gọi `sungen audit` trong vòng repair. Skill cũ giữ làm Guide.
16
+
17
+ ---
18
+
19
+ ## 1. Trách nhiệm của Orchestration (Orchestrator làm gì)
20
+
21
+ Orchestrator KHÔNG sinh testcase trực tiếp. Nó trả lời và điều phối theo các câu hỏi:
22
+
23
+ | # | Câu hỏi | Thành phần phụ trách | Output |
24
+ |---|---|---|---|
25
+ | 1 | Goal là gì? Loại test gì (web-ui/api/mobile/perf)? | Goal Resolver | `goal` |
26
+ | 2 | Có những input nào? Đầy đủ tới đâu? Mâu thuẫn gì? | **Context Discovery** | `discovery-report` |
27
+ | 3 | Nên đi route nào (và song song không)? | **Route Planner** | `route-plan` |
28
+ | 4 | Sinh viewpoint-overview + Gherkin | AI (qua Guide skill) | `viewpoint-overview`, `.feature` |
29
+ | 5 | Output đã đủ tốt chưa? Thiếu/trùng/nông ở đâu? | **Harness Sensors** | `audit-report` |
30
+ | 6 | Chưa đạt → sửa ở đâu, sinh bù gì? | **Repair Loop** | artifact mới |
31
+ | 7 | Spec đã đổi chưa? Phần nào tái dùng/tái tạo? | **Change Planner** | `change-plan` |
32
+ | 8 | Tốn bao nhiêu tài nguyên, ở bước nào? | **Usage Ledger** | `usage-report` |
33
+ | 9 | QA quyết định gì? Học được gì? | **QA Checkpoint + Feedback** | `feedback.jsonl` |
34
+
35
+ **Bất biến:** Orchestrator dừng ở ranh giới artifact (`.feature`/`test-data`/`selectors`). Từ đó trở đi là `generate → run-test (auto-fix) → delivery` — **không đổi**.
36
+
37
+ ---
38
+
39
+ ## 2. FLOW CHÍNH (Main Flow) — Spec-first, web-ui
40
+
41
+ ```
42
+ ┌─────────────────────────────┐
43
+ QA cung cấp: │ /sungen:design <screen> │
44
+ spec.md, figma, │ (Orchestrator entrypoint) │
45
+ screenshot, url, └──────────────┬──────────────┘
46
+ prompt │
47
+
48
+ ┌─────────────────────────────┐
49
+ (1) │ CONTEXT DISCOVERY │ đọc MỌI nguồn, không bắt chọn
50
+ │ - liệt kê nguồn + độ đầy đủ │
51
+ │ - phát hiện mâu thuẫn │
52
+ │ - RECOMMEND route + nguồn ưu │──► discovery-report.json
53
+ └──────────────┬──────────────┘
54
+
55
+ ┌─────────────────────────────┐
56
+ (2) │ ROUTE PLANNER │ mặc định: Spec-first
57
+ │ chọn 1..n route (song song) │──► route-plan.json
58
+ └──────────────┬──────────────┘
59
+
60
+ ┌─────────────────────────────┐
61
+ (3) │ VIEWPOINT PLANNER (AI+Guide) │ LUÔN sinh overview trước
62
+ │ → viewpoint-overview │──► test-viewpoint.md
63
+ │ (id, priority, evidence) │
64
+ └──────────────┬──────────────┘
65
+
66
+ ┌─────────────────────────────┐
67
+ (4) │ VIEWPOINT GATE (Sensor) │◄── seed Universal Catalog (reference)
68
+ │ Required/High đã đủ chưa? │
69
+ └───────┬──────────────┬───────┘
70
+ FAIL │ │ PASS
71
+ (sinh bù viewpoint) ▼
72
+ ┌─────────────────────────────┐
73
+ (5) │ GHERKIN GENERATOR (AI+Guide)│ Tier critical+high trước
74
+ │ → .feature + test-data │
75
+ └──────────────┬──────────────┘
76
+
77
+ ┌─────────────────────────────┐
78
+ (6) │ HARNESS SENSORS (sungen audit) │
79
+ │ ① traceability ② coverage-balance │──► audit-report.json
80
+ │ ③ assertion-depth ④ duplicate │ + score
81
+ └───────┬──────────────────────┬─────────┘
82
+ FAIL │ │ PASS
83
+ ▼ │
84
+ ┌───────────────┐ │
85
+ (7) │ REPAIR LOOP │ ngân sách N vòng
86
+ │ sửa đúng chỗ │ (vd: sinh bù VP-DATA-CONSISTENCY,
87
+ │ sensor chỉ ra │ nâng assertion nông → có {{data}})
88
+ └──────┬────────┘
89
+ └──────────► quay lại (5)/(6)
90
+ │ (đạt gate hoặc hết ngân sách → báo gap)
91
+
92
+ ┌─────────────────────────────┐
93
+ (8) │ CONVERGE + LEDGER + CHECKPOINT │
94
+ │ artifact contract đầy đủ + usage-report │
95
+ │ + manifest (scenario↔vp↔spec-hash) │
96
+ │ → QA review/feedback │
97
+ └──────────────┬──────────────┘
98
+
99
+ [BẤT BIẾN] generate → run-test(auto-fix) → delivery → dashboard
100
+ ```
101
+
102
+ Điểm khác biệt cốt lõi so với hiện tại: **(1) Discovery + recommend** thay cho hỏi-chọn; **(4) Gate** + **(6) Sensors** thay cho "nhờ AI tự đủ"; **(7) Repair** có ngân sách; **(8) Ledger + manifest** để đo & tái dùng.
103
+
104
+ ---
105
+
106
+ ## 3. FLOW BỔ SUNG (Supplementary Routes)
107
+
108
+ Tất cả route hội tụ về cùng **artifact contract** (Mục 6). Route chỉ khác ở *nguồn tri thức đầu vào*.
109
+
110
+ | Route | Khi nào kích hoạt | Khác biệt so với main |
111
+ |---|---|---|
112
+ | **A. Spec-first** (mặc định) | spec.md đủ tốt | Như Mục 2 |
113
+ | **B. Source-first** | spec mỏng/thiếu, có source code | Bước (3) thay bằng *Source Behavior Miner*: trích endpoint/screen/state/validation/permission từ code → viewpoint |
114
+ | **C. History-first** | có testcase cũ tương tự (cần Lớp 2/feedback) | Bước (3): search cluster testcase cũ → trích viewpoint chung → recommend |
115
+ | **D. Defect-first** | feature vùng rủi ro cao (cần defect history) | Ưu tiên viewpoint từng bắt defect; risk-based |
116
+ | **E. QA-guided** | QA bổ sung viewpoint / AI confidence thấp | QA thêm viewpoint → phân loại → check trùng → sinh variant |
117
+
118
+ **Parallel execution:** khi nhiều nguồn cùng có (vd spec + source + history), Route Planner chạy song song (Agent đọc spec / Agent đọc source / Agent search history) → **merge viewpoint candidates → khử trùng → rank theo risk/coverage/evidence**. Đây cũng là cách trả lời yêu cầu "recommend thay vì bắt chọn": tất cả nguồn được *gộp*, không chọn 1.
119
+
120
+ ---
121
+
122
+ ## 4. CÂY QUYẾT ĐỊNH KHI GẶP HẠN CHẾ (Discovery / Fallback)
123
+
124
+ > "Khi gặp hạn chế thì khám phá hoặc phát triển theo flow nào" — đây là logic fallback của Orchestrator. Nguyên tắc: **không đứt giữa chừng**; luôn có bước kế tiếp; nếu bí thì *output assumption rõ ràng* thay vì im lặng.
125
+
126
+ ```
127
+ Bắt đầu: cần sinh viewpoint/Gherkin cho <screen>
128
+
129
+ ├─ spec.md đủ tốt? ───────────── YES ─► Route A (Spec-first)
130
+ │ │ NO
131
+ │ ▼
132
+ ├─ có source code? ───────────── YES ─► Route B (Source-first): mine behavior từ code
133
+ │ │ NO
134
+ │ ▼
135
+ ├─ có testcase/lịch sử tương tự? ─ YES ─► Route C (History-first): trích viewpoint từ cluster
136
+ │ │ NO
137
+ │ ▼
138
+ ├─ thuộc domain rủi ro + có defect history? ─ YES ─► Route D (Defect-first)
139
+ │ │ NO
140
+ │ ▼
141
+ └─ HỎI QA (Route E) ─► QA chưa phản hồi? ─► OUTPUT với ASSUMPTION LIST rõ ràng (đánh dấu cần xác minh)
142
+
143
+ Trong quá trình chạy, nếu Sensor (bước 6) FAIL:
144
+
145
+ ├─ Viewpoint Gate FAIL (thiếu Required/High) ─► Repair: sinh bù viewpoint + scenario cho VP thiếu
146
+ ├─ Assertion-depth FAIL (quá nhiều Then nông) ─► Repair: nâng assertion (thêm {{data}}/observable), hoặc
147
+ │ nếu thiếu data cụ thể → cần spec/flow ► gợi ý add-flow (cross-screen)
148
+ ├─ Coverage-balance FAIL (VP phụ expand sâu, VP High mỏng) ─► Repair: dừng expand VP phụ, sinh VP High
149
+ ├─ Duplicate FAIL (cluster trùng nghĩa) ─► Repair: merge/loại, giữ 1 đại diện + ghi rõ EP variants
150
+ └─ Traceability FAIL (scenario không map viewpoint) ─► Repair: gắn vp-id vào scenario
151
+
152
+ Hết ngân sách repair (N vòng) mà vẫn FAIL:
153
+ └─► KHÔNG lặp vô hạn. Ghi gap vào audit-report + đưa QA checkpoint (human-in-the-loop).
154
+ ```
155
+
156
+ **Quy tắc "khám phá thêm khi nông":** nếu assertion-depth sensor báo một viewpoint critical chỉ assert "see page/section" (vụ home: Cart/Detail/Filter), Orchestrator phải nhận diện đây là **cross-screen** → khuyến nghị chuyển sang **flow** (`add-flow`) và/hoặc cần **năng lực DSL capture biến** (xem `reports/sungen_refactor_spec.md` §5.4) — thay vì giả vờ pass bằng assertion nông.
157
+
158
+ ---
159
+
160
+ ## 5. HARNESS — Guide / Sensor / Repair / Checkpoint
161
+
162
+ ### 5.1 Guide (AI, giữ nguyên skill markdown)
163
+ Skill hiện có (`sungen-tc-generation`, `sungen-viewpoint`, `sungen-test-design-techniques`) là **nửa Guide** — steer AI làm đúng từ đầu. **Giữ**, nhưng đồng bộ threshold với Sensor.
164
+
165
+ ### 5.2 Sensor (tất định — code MỚI, phần lõi refactor)
166
+ Bốn sensor + một gate. Input: `.feature` (qua GherkinParser) + `test-viewpoint.md` + (optional) `spec.md`.
167
+
168
+ | Sensor | Đo | Output | Tất định? |
169
+ |---|---|---|---|
170
+ | **Viewpoint Gate** | mỗi viewpoint Required/High có ≥1 scenario phủ | pass/fail + danh sách thiếu | heuristic keyword (hybrid) |
171
+ | **Traceability** | scenario map được tới viewpoint id; có evidence | % traceable | tất định |
172
+ | **Coverage-balance** | phân bố scenario theo VP-group & priority; phát hiện lệch | cảnh báo imbalance | tất định |
173
+ | **Assertion-depth** | tỉ lệ `Then` nông (chỉ see page/section) vs có data assert | % shallow | tất định |
174
+ | **Duplicate** | cluster scenario cùng shape/nghĩa | cluster + đề xuất | heuristic |
175
+
176
+ > Trung thực: Gate & Duplicate (ngữ nghĩa) là **hybrid** — heuristic rẻ chặn phần rõ, AI-judge cho vùng xám. Không hứa thuần tất định cho ngữ nghĩa.
177
+
178
+ ### 5.3 Repair Loop
179
+ - Sensor trả **phản hồi cụ thể** (vd: "VP-DATA-CONSISTENCY Required nhưng 0 scenario; gợi ý: add-flow checkout").
180
+ - Orchestrator đưa phản hồi này lại bước sinh. **Ngân sách N vòng** (mặc định 2–3, như vòng auto-fix selector). Hết ngân sách → báo gap, không lặp vô hạn.
181
+
182
+ ### 5.4 QA Checkpoint
183
+ - QA accept/reject/edit/add viewpoint. Mọi quyết định ghi `feedback.jsonl` (xem `reports/sungen_refactor_spec.md` §8).
184
+
185
+ ---
186
+
187
+ ## 5b. Agent layer (tầng thứ 4 — isolation / independence / parallel)
188
+
189
+ Prompt (command) + Skill + Harness (deterministic) là đủ cho work tuần tự. **Agent** (context LLM riêng) chỉ thêm khi cần **isolation**, **góc nhìn độc lập**, hoặc **parallel**. Nguyên tắc chọn:
190
+
191
+ | Loại | Khi nào | Sungen |
192
+ |---|---|---|
193
+ | Deterministic (code) | đo/gate/fingerprint | `sungen audit/manifest/ledger` — KHÔNG agent-hóa |
194
+ | Skill (tri thức) | quy tắc context chính theo | gherkin-syntax, tc-generation, viewpoint, harness-audit |
195
+ | **Agent (context riêng)** | khám phá tốn token cần tóm gọn; cần phán xử độc lập; chạy song song | **discovery, reviewer** |
196
+
197
+ > "Senior QA" đã là role-prompt trong command — KHÔNG cần agent riêng cho nó.
198
+
199
+ **Hai agent (Claude Code sub-agent; Copilot chạy inline, fallback tuần tự):**
200
+
201
+ 1. **`sungen-reviewer` (Judge — đòn bẩy cao nhất).** Reviewer độc lập, KHÔNG viết test, chỉ phán xử **ngữ nghĩa** mà gate tất định bỏ sót: steps có *chứng minh* title/viewpoint không; `Then` có observable không; business-critical có assertion sâu không; `@manual` có chính đáng không. Trả `VERDICT + ISSUES(+fix)`. Nhúng vào **repair loop** (step 5.5): `audit (structural) → reviewer (semantic) → merge → repair`. Generator và reviewer **tách context** → không rubber-stamp. Giải đúng vấn đề kẹt ~6.7 ("title đúng nhưng steps chưa prove").
202
+
203
+ 2. **`sungen-discovery` (Explorer).** Đọc mọi nguồn (spec/figma/ui/live) trong **context riêng**, trả **discovery-report cô đọng** (sources/completeness/conflicts/route/key-facts) → orchestrator chính giữ context sạch để generate. Dùng ở bước discovery.
204
+
205
+ **Lưu ý:** mỗi agent tốn token → **ledger phải đo per-agent**; output agent phải có **schema rõ** (verdict / discovery-report) tránh chat lan man; **parallel route agents** (spec/source/history/defect) hoãn tới khi multi-source thật sự cần.
206
+
207
+ ## 6. Convergence Contract (artifact bắt buộc — mọi route hội tụ về đây)
208
+
209
+ ```
210
+ qa/screens/<screen>/requirements/test-viewpoint.md # viewpoint-overview (id, priority, evidence)
211
+ qa/screens/<screen>/features/<screen>.feature # Gherkin (format hiện tại — downstream không đổi)
212
+ qa/screens/<screen>/test-data/<screen>.yaml
213
+ .sungen/manifest/<screen>.json # scenario ↔ viewpoint ↔ spec-section fingerprint (cho reuse)
214
+ .sungen/reports/<screen>-audit.json # sensor + gate + score
215
+ .sungen/reports/<screen>-usage.json # token/cost/time ledger theo bước
216
+ .sungen/feedback/*.jsonl # QA feedback (local-first)
217
+ ```
218
+
219
+ `.feature`/`test-data`/`selectors` giữ schema cũ → `generate`/`delivery` không đổi.
220
+
221
+ ---
222
+
223
+ ## 7. Orchestrator command (AI) — `/sungen:design` (sketch)
224
+
225
+ Lệnh AI mới điều phối flow chính; thay thế dần phần "hỏi-chọn" trong `create-test`. (`create-test` cũ vẫn chạy được — `design` là superset có harness.)
226
+
227
+ ```
228
+ Role: Test Design Orchestrator.
229
+ Steps:
230
+ 1. CONTEXT DISCOVERY: đọc spec.md, spec_figma.md, ui/*, (live url nếu có). KHÔNG hỏi "chọn nguồn".
231
+ → Tóm tắt nguồn + độ đầy đủ + mâu thuẫn + RECOMMEND route. Trình bày recommendation (QA override được).
232
+ 2. VIEWPOINT PLANNER: sinh test-viewpoint.md (id/priority/evidence) — LUÔN làm, kể cả có sẵn (diff & bổ sung).
233
+ 3. Chạy `sungen audit --screen <s> --gate viewpoint` → nếu thiếu Required/High: sinh bù.
234
+ 4. GHERKIN: sinh .feature + test-data (critical+high trước), gắn vp-id (traceability).
235
+ 5. Chạy `sungen audit --screen <s>` (full). Đọc audit-report.
236
+ 6. REPAIR (≤ N vòng): sửa đúng chỗ sensor chỉ ra. Hết ngân sách → báo gap.
237
+ 7. Ghi usage-ledger + manifest. Trình bày score + gap cho QA (checkpoint).
238
+ 8. AskUserQuestion next: review / run-test / feedback.
239
+ ```
240
+
241
+ Skill được auto-load: `sungen-viewpoint`, `sungen-tc-generation`, `sungen-test-design-techniques`, `sungen-gherkin-syntax` (Guide); `sungen-harness-audit` (mới — mô tả cách đọc audit-report & repair).
242
+
243
+ ---
244
+
245
+ ## 8. Mở rộng (test-type packs + executor providers)
246
+
247
+ Orchestrator/Harness/Discovery là **chung**. Chỉ thay theo loại test:
248
+ - **Test-type pack** = (viewpoint catalog + sensor set + contract): `web-ui`, `mobile-ui`, `api`, `performance`, `db-verify`.
249
+ - **Executor provider** = sinh code: Playwright / Appium / k6 / SQL runner (xem `reports/mobile-proposal.md`).
250
+
251
+ Ví dụ: pack `performance` thay Assertion-depth sensor bằng "Threshold sensor" (có p95/error-rate?), Viewpoint Gate dùng catalog load-profile. Cùng một orchestration loop.
252
+
253
+ ---
254
+
255
+ ## 9. Phase triển khai (không phá luồng cũ)
256
+
257
+ ```
258
+ P1 (refactor lõi, TẤT ĐỊNH — làm trong lần này):
259
+ sungen audit = 4 sensor + viewpoint gate + score + report JSON + seed catalog
260
+ → verify trên examples/automationexercise home: phải tái hiện gap 6.5/10
261
+ P2: /sungen:design (orchestrator AI) gọi audit trong repair loop; discovery+recommend
262
+ P3: manifest + spec-fingerprint (reuse); usage ledger
263
+ P4: feedback local (Nấc 1)
264
+ P5: test-type packs (api/perf/db) + executor providers (Appium…)
265
+ ```
266
+
267
+ P1 là phần code của lần refactor này (Mục 5.2). Các phase sau bám thin-first: 1 workflow thật → học → mở rộng.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sungen",
3
- "version": "2.7.0-beta.1",
3
+ "version": "3.0.0-beta.71",
4
4
  "description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc && npm run copy-templates",
12
- "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html",
12
+ "copy-templates": "mkdir -p dist/generators/test-generator/adapters/playwright/templates/steps && mkdir -p dist/generators/test-generator/templates && mkdir -p dist/orchestrator/templates && mkdir -p dist/dashboard/templates && cp -r src/generators/test-generator/adapters/playwright/templates/*.hbs dist/generators/test-generator/adapters/playwright/templates/ 2>/dev/null || true && cp -r src/generators/test-generator/adapters/playwright/templates/steps dist/generators/test-generator/adapters/playwright/templates/ && cp src/generators/test-generator/templates/*.hbs dist/generators/test-generator/templates/ 2>/dev/null || true && cp -r src/orchestrator/templates/* dist/orchestrator/templates/ && cp src/dashboard/templates/index.html dist/dashboard/templates/index.html && mkdir -p dist/harness/catalog && cp src/harness/catalog/*.yaml dist/harness/catalog/",
13
13
  "build:dashboard": "cd dashboard && npm install --silent && npm run build && cd .. && cp dashboard/dist/index.html src/dashboard/templates/index.html",
14
14
  "dev": "tsx src/cli/index.ts",
15
15
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -26,7 +26,7 @@
26
26
  "automation",
27
27
  "qa"
28
28
  ],
29
- "author": "ee team (engineer-excellence) — Sun Asterisk",
29
+ "author": "eqe team (engineer & quality) — Sun Asterisk",
30
30
  "license": "MIT",
31
31
  "engines": {
32
32
  "node": ">=18.0.0"
@@ -47,7 +47,7 @@
47
47
  "handlebars": "^4.7.8",
48
48
  "ora": "^9.0.0",
49
49
  "plop": "^4.0.4",
50
- "tsx": "^4.21.0",
50
+ "tsx": "^4.22.4",
51
51
  "yaml": "^2.8.2",
52
52
  "zod": "^4.1.13"
53
53
  },
@@ -67,11 +67,15 @@
67
67
  },
68
68
  "overrides": {
69
69
  "brace-expansion": "^5.0.6",
70
- "uuid": "^11.1.1"
70
+ "uuid": "^11.1.1",
71
+ "tmp": "^0.2.6",
72
+ "esbuild": "^0.28.1"
71
73
  },
72
74
  "resolutions": {
73
75
  "brace-expansion": "^5.0.6",
74
- "uuid": "^11.1.1"
76
+ "uuid": "^11.1.1",
77
+ "tmp": "^0.2.6",
78
+ "esbuild": "^0.28.1"
75
79
  },
76
80
  "files": [
77
81
  "dist",
@@ -66,15 +66,15 @@ export function registerAddCommand(program: Command): void {
66
66
  console.log(` qa/screens/${options.screen}/requirements/spec.md (stub created)`);
67
67
  }
68
68
 
69
- // Loud banner: Tell the calling agent to
70
- // invoke the `sungen-figma-source` skill to synthesize the narrative sections in spec_figma.md, since
69
+ // Loud banner: Tell the calling agent to invoke the `sungen-capture` skill
70
+ // (mode figma-pat) to synthesize the narrative sections in spec_figma.md, since
71
71
  // the CLI cannot do this (synthesis is LLM-driven off the cached raw JSON).
72
72
  console.log('');
73
73
  console.log('==============================================================');
74
74
  console.log(' NEXT STEP (REQUIRED — DO NOT SKIP)');
75
75
  console.log('==============================================================');
76
76
  console.log(' spec_figma.md envelope is written but narrative is EMPTY.');
77
- console.log(' Invoke the `sungen-figma-source` skill NOW to synthesize');
77
+ console.log(' Invoke `sungen-capture` (mode figma-pat) NOW to synthesize');
78
78
  console.log(' the 7 sections (Purpose / ASCII Layout / Regions / Actions /');
79
79
  console.log(' Form Fields / Data Columns / Navigation) below the');
80
80
  console.log(' `<!-- SYNTHESIS-BELOW -->` marker.');
@@ -0,0 +1,92 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { runAudit, AuditReport } from '../../harness/audit';
5
+
6
+ function findScreenDir(name: string): string | null {
7
+ const screen = path.join(process.cwd(), 'qa', 'screens', name);
8
+ if (fs.existsSync(screen)) return screen;
9
+ const flow = path.join(process.cwd(), 'qa', 'flows', name);
10
+ if (fs.existsSync(flow)) return flow;
11
+ return null;
12
+ }
13
+
14
+ function bar(ratio: number, width = 20): string {
15
+ const n = Math.round(ratio * width);
16
+ return '█'.repeat(n) + '░'.repeat(width - n);
17
+ }
18
+
19
+ function render(r: AuditReport): void {
20
+ const L = console.log;
21
+ L('');
22
+ L(`━━━ Harness Audit: ${r.screen} (${r.scenarioCount} scenarios) ━━━`);
23
+ L('');
24
+ L(` Quality score (business-weighted): ${r.score.overall}/10 [Viewpoint Gate: ${r.gateStatus}]`);
25
+ L(` Intent: focus=${r.intent.focus} · depth threshold ${r.depth.threshold.toFixed(2)} (${r.intent.source})`);
26
+ L(` coverage ${bar(r.score.coverage)} ${(r.score.coverage * 100).toFixed(0)}%`);
27
+ L(` businessDepth ${bar(r.score.businessDepth)} ${(r.score.businessDepth * 100).toFixed(0)}% [${r.depth.verdict.toUpperCase()}]`);
28
+ L(` balance ${bar(r.score.balance)} ${(r.score.balance * 100).toFixed(0)}%`);
29
+ L(` traceability ${bar(r.score.traceability)} ${(r.score.traceability * 100).toFixed(0)}%`);
30
+ L(` (${r.score.formula})`);
31
+ L('');
32
+ L(` ① Viewpoint Gate — page-type: ${r.gate.pageType ?? 'unknown'} — themes ${r.gate.themesCovered}/${r.gate.themesTotal} covered`);
33
+ if (r.gate.gaps.length) for (const g of r.gate.gaps) L(` ✗ ${g.status === 'shallow' ? 'SHALLOW' : 'MISSING'}: ${g.theme}`);
34
+ else L(' ✓ all critical themes covered (with data assertions)');
35
+ L('');
36
+ L(` ② Assertion depth — ${r.depth.businessCriticalShallow}/${r.depth.businessCriticalTotal} business-critical scenarios are shallow (visibility/nav only)`);
37
+ for (const s of r.depth.shallowBusinessCritical.slice(0, 8)) L(` ⚠ ${s.category}: ${s.name}`);
38
+ if (r.depth.shallowBusinessCritical.length > 8) L(` … +${r.depth.shallowBusinessCritical.length - 8} more`);
39
+ L('');
40
+ L(` ③ Coverage balance — ${r.balance.imbalanced ? '⚠ IMBALANCED' : '✓ balanced'}`);
41
+ L(` buckets: ${Object.entries(r.balance.byBucket).map(([k, v]) => `${k}=${v}`).join(' ')}`);
42
+ L('');
43
+ L(` ④ Duplicate — ${r.duplicates.clusters.length} same-shape cluster(s), ${r.duplicates.exactDuplicateCount} likely exact dup(s)`);
44
+ for (const c of r.duplicates.clusters.slice(0, 3)) {
45
+ L(` ${c.sameDataLikely ? '✗ exact' : '○ EP/data family'} (${c.scenarios.length}): ${c.scenarios.slice(0, 3).join(' | ')}${c.scenarios.length > 3 ? ' …' : ''}`);
46
+ }
47
+ L('');
48
+ L(` ⑤ Traceability — ${(r.trace.mappedRatio * 100).toFixed(0)}% scenarios linked to viewpoint-overview`);
49
+ L(` ${r.trace.note}`);
50
+ L('');
51
+ L(' ── Findings (Repair targets) ──');
52
+ if (r.findings.length === 0) L(' ✓ none — output passes the harness');
53
+ for (const f of r.findings) L(` • ${f}`);
54
+ L('');
55
+ }
56
+
57
+ export function registerAuditCommand(program: Command): void {
58
+ program
59
+ .command('audit')
60
+ .description('Harness: measure test-design quality (viewpoint gate, depth, balance, duplicates, traceability)')
61
+ .option('-s, --screen <name>', 'Screen or flow name to audit')
62
+ .option('--json', 'Output the raw JSON report')
63
+ .action((options) => {
64
+ try {
65
+ const name = options.screen;
66
+ if (!name) throw new Error('Provide --screen <name>');
67
+ const dir = findScreenDir(name);
68
+ if (!dir) throw new Error(`Screen/flow not found: qa/screens/${name} or qa/flows/${name}`);
69
+
70
+ const report = runAudit(dir, name);
71
+
72
+ // Persist report under .sungen/reports/
73
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
74
+ fs.mkdirSync(outDir, { recursive: true });
75
+ const outPath = path.join(outDir, `${name}-audit.json`);
76
+ fs.writeFileSync(outPath, JSON.stringify(report, null, 2), 'utf-8');
77
+
78
+ if (options.json) {
79
+ console.log(JSON.stringify(report, null, 2));
80
+ } else {
81
+ render(report);
82
+ console.log(` Report: ${path.relative(process.cwd(), outPath)}`);
83
+ console.log('');
84
+ }
85
+ // Non-zero exit when the gate fails — usable in CI / repair loop.
86
+ process.exit(report.gateStatus === 'FAIL' ? 2 : 0);
87
+ } catch (error) {
88
+ console.error('Error:', error instanceof Error ? error.message : error);
89
+ process.exit(1);
90
+ }
91
+ });
92
+ }
@@ -0,0 +1,48 @@
1
+ import { Command } from 'commander';
2
+ import { addBlindspot, listBlindspots, blindspotsForPrompt } from '../../harness/blindspot';
3
+
4
+ export function registerBlindspotCommand(program: Command): void {
5
+ const bs = program
6
+ .command('blindspot')
7
+ .description('Blind-Spot Memory: reusable patterns so the harness stops repeating a known miss');
8
+
9
+ bs
10
+ .command('add')
11
+ .description('Record a blind-spot pattern (promote a recurring miss to a reusable rule)')
12
+ .requiredOption('--pattern <name>', 'Short pattern name, e.g. "add-action-no-duplicate-check"')
13
+ .requiredOption('--rule <text>', 'The generalised rule to apply next time')
14
+ .option('--example <text>', 'A concrete instance that motivated it')
15
+ .option('--screen <name>', 'Where it was first noticed')
16
+ .option('--source <who>', 'qa | challenge | feedback', 'qa')
17
+ .action((o) => {
18
+ try {
19
+ addBlindspot({ pattern: o.pattern, rule: o.rule, example: o.example, screen: o.screen, source: o.source });
20
+ console.log(`✓ blindspot recorded: [${o.pattern}]`);
21
+ } catch (e) {
22
+ console.error('Error:', e instanceof Error ? e.message : e);
23
+ process.exit(1);
24
+ }
25
+ });
26
+
27
+ bs
28
+ .command('list')
29
+ .description('List recorded blind-spot patterns')
30
+ .option('--prompt', 'Output the compact bullet form used in generator/critic prompts')
31
+ .action((o) => {
32
+ try {
33
+ if (o.prompt) { const s = blindspotsForPrompt(); console.log(s || '(none)'); return; }
34
+ const all = listBlindspots();
35
+ if (!all.length) { console.log('No blind-spots recorded yet. Add one with `sungen blindspot add`.'); return; }
36
+ console.log(`\n━━━ Blind-Spot Memory (${all.length}) ━━━`);
37
+ for (const b of all) {
38
+ console.log(` • [${b.pattern}]${b.screen ? ` (${b.screen})` : ''}`);
39
+ console.log(` rule: ${b.rule}`);
40
+ if (b.example) console.log(` e.g.: ${b.example}`);
41
+ }
42
+ console.log('');
43
+ } catch (e) {
44
+ console.error('Error:', e instanceof Error ? e.message : e);
45
+ process.exit(1);
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,55 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { buildChallenge, renderChallengeMarkdown } from '../../harness/challenge';
5
+
6
+ function findScreenDir(name: string): string | null {
7
+ const screen = path.join(process.cwd(), 'qa', 'screens', name);
8
+ if (fs.existsSync(screen)) return screen;
9
+ const flow = path.join(process.cwd(), 'qa', 'flows', name);
10
+ if (fs.existsSync(flow)) return flow;
11
+ return null;
12
+ }
13
+
14
+ export function registerChallengeCommand(program: Command): void {
15
+ program
16
+ .command('challenge')
17
+ .description('Exploration mode (Loop 2): attack the existing suite for blind spots — advisory, never auto-merges')
18
+ .requiredOption('-s, --screen <name>', 'Screen or flow name')
19
+ .option('--json', 'Output the raw JSON report')
20
+ .action((o) => {
21
+ try {
22
+ const dir = findScreenDir(o.screen);
23
+ if (!dir) throw new Error(`Screen/flow not found: qa/screens/${o.screen} or qa/flows/${o.screen}`);
24
+ const report = buildChallenge(dir, o.screen);
25
+
26
+ const outDir = path.join(process.cwd(), '.sungen', 'reports');
27
+ fs.mkdirSync(outDir, { recursive: true });
28
+ const md = renderChallengeMarkdown(report);
29
+ const outPath = path.join(outDir, `${o.screen}-challenge.md`);
30
+ fs.writeFileSync(outPath, md, 'utf-8');
31
+
32
+ if (o.json) { console.log(JSON.stringify(report, null, 2)); return; }
33
+
34
+ const L = console.log;
35
+ L(`\n━━━ Challenge (exploration mode): ${report.screen} ━━━`);
36
+ L(' Advisory — does NOT change the official suite; surfaces what production missed.\n');
37
+ L(' ① Depth — title claims a collection but asserts a single element');
38
+ if (report.collectionClaimSingular.length) {
39
+ for (const f of report.collectionClaimSingular) L(` ⚠ ${f.scenario}\n → ${f.suggestion}`);
40
+ } else L(' ✓ none');
41
+ L(' ② Coverage — over-covered / shallow');
42
+ if (report.overCovered.length) for (const o2 of report.overCovered) L(` • ${o2.bucket}: ${o2.note}`);
43
+ if (report.shallowThemes.length) L(` • shallow themes: ${report.shallowThemes.join(', ')}`);
44
+ if (!report.overCovered.length && !report.shallowThemes.length) L(' ✓ balanced');
45
+ L(' ③ Novelty — prompts for the `sungen-challenge` agent (≤20% of official, no auto-merge)');
46
+ for (const p of report.noveltyPrompts) L(` • ${p}`);
47
+ L(' ── Exploration readiness ──');
48
+ for (const e of report.explorationReadiness) L(` • ${e}`);
49
+ L(`\n Report: ${path.relative(process.cwd(), outPath)}\n`);
50
+ } catch (error) {
51
+ console.error('Error:', error instanceof Error ? error.message : error);
52
+ process.exit(1);
53
+ }
54
+ });
55
+ }
@@ -0,0 +1,65 @@
1
+ import { Command } from 'commander';
2
+ import { recordFeedback, summarize, FeedbackType, FeedbackDecision } from '../../harness/feedback';
3
+
4
+ const TYPES = ['test-design', 'product', 'other'];
5
+ const DECISIONS = ['accept', 'reject', 'edit', 'add', 'none'];
6
+
7
+ export function registerFeedbackCommand(program: Command): void {
8
+ const fb = program
9
+ .command('feedback')
10
+ .description('Local-first QA feedback (test-design knowledge + product telemetry). Synced later when a server exists.');
11
+
12
+ fb
13
+ .command('record')
14
+ .description('Record a feedback entry')
15
+ .requiredOption('--message <text>', 'The feedback')
16
+ .option('--type <t>', `Feedback type (${TYPES.join('|')})`, 'test-design')
17
+ .option('-s, --screen <name>', 'Screen/flow the feedback relates to')
18
+ .option('--target <ref>', 'What it is about (viewpoint id / scenario / command / artifact)')
19
+ .option('--decision <d>', `QA decision (${DECISIONS.join('|')})`, 'none')
20
+ .option('--reason <text>', 'Why')
21
+ .option('--source <who>', 'Who gave the feedback', 'qa')
22
+ .action((o) => {
23
+ try {
24
+ if (!TYPES.includes(o.type)) throw new Error(`--type must be one of ${TYPES.join(', ')}`);
25
+ if (!DECISIONS.includes(o.decision)) throw new Error(`--decision must be one of ${DECISIONS.join(', ')}`);
26
+ const p = recordFeedback({
27
+ type: o.type as FeedbackType,
28
+ screen: o.screen,
29
+ target: o.target,
30
+ decision: o.decision as FeedbackDecision,
31
+ message: o.message,
32
+ reason: o.reason,
33
+ source: o.source,
34
+ });
35
+ console.log(`✓ feedback recorded (${o.type}${o.screen ? ' · ' + o.screen : ''}) → ${p}`);
36
+ } catch (e) {
37
+ console.error('Error:', e instanceof Error ? e.message : e);
38
+ process.exit(1);
39
+ }
40
+ });
41
+
42
+ fb
43
+ .command('list')
44
+ .description('List / summarise recorded feedback')
45
+ .option('-s, --screen <name>', 'Filter by screen')
46
+ .option('--type <t>', 'Filter by type')
47
+ .option('--json', 'Output raw JSON')
48
+ .action((o) => {
49
+ try {
50
+ const s = summarize({ screen: o.screen, type: o.type });
51
+ if (o.json) { console.log(JSON.stringify(s, null, 2)); return; }
52
+ console.log(`\n━━━ Feedback (${s.total}) ━━━`);
53
+ console.log(` by type: ${Object.entries(s.byType).map(([k, v]) => `${k}=${v}`).join(' ') || '—'}`);
54
+ console.log(` by decision: ${Object.entries(s.byDecision).map(([k, v]) => `${k}=${v}`).join(' ') || '—'}\n`);
55
+ for (const e of s.entries.slice(-20)) {
56
+ console.log(` [${e.type}${e.decision && e.decision !== 'none' ? '/' + e.decision : ''}] ${e.screen ? e.screen + ' · ' : ''}${e.target ? e.target + ' — ' : ''}${e.message}`);
57
+ if (e.reason) console.log(` reason: ${e.reason}`);
58
+ }
59
+ console.log('');
60
+ } catch (e) {
61
+ console.error('Error:', e instanceof Error ? e.message : e);
62
+ process.exit(1);
63
+ }
64
+ });
65
+ }
@@ -2,6 +2,7 @@ import { Command } from 'commander';
2
2
  import * as path from 'path';
3
3
  import * as fs from 'fs';
4
4
  import { CodeGenerator } from '../../generators/test-generator/code-generator';
5
+ import { scanTestDataSecrets } from '../../harness/secret-scan';
5
6
 
6
7
  /**
7
8
  * Find .feature files recursively in a directory
@@ -137,6 +138,24 @@ export function registerGenerateCommand(program: Command): void {
137
138
  );
138
139
 
139
140
  console.log(`\n${results.length} test file(s) generated.`);
141
+
142
+ // Security S0 — warn (never block) if test-data looks to hold a real secret.
143
+ const scanDirs: string[] = [];
144
+ if (screenName) scanDirs.push(path.join(process.cwd(), 'qa', 'screens', screenName));
145
+ else if (flowName) scanDirs.push(path.join(process.cwd(), 'qa', 'flows', flowName));
146
+ else {
147
+ for (const base of ['screens', 'flows']) {
148
+ const d = path.join(process.cwd(), 'qa', base);
149
+ if (fs.existsSync(d)) for (const n of fs.readdirSync(d)) scanDirs.push(path.join(d, n));
150
+ }
151
+ }
152
+ const secretHits = scanDirs.flatMap((d) => scanTestDataSecrets(d));
153
+ if (secretHits.length) {
154
+ console.log(`\n⚠️ Possible secret(s) in committed test-data (review — do NOT commit real credentials):`);
155
+ for (const h of secretHits.slice(0, 10)) console.log(` ${h.file}:${h.line} — ${h.reason}`);
156
+ console.log(` Move real secrets to an env overlay / CI secret; keep test-data placeholders only.`);
157
+ }
158
+
140
159
  console.log(`Next step: npx playwright test --ui\n`);
141
160
  } catch (error) {
142
161
  console.error('Error:', error instanceof Error ? error.message : error);