@sun-asterisk/sungen 3.2.2 → 3.2.3-beta.3

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 (355) hide show
  1. package/dist/capabilities/discover.d.ts.map +1 -1
  2. package/dist/capabilities/discover.js +26 -3
  3. package/dist/capabilities/discover.js.map +1 -1
  4. package/dist/cli/commands/add-flow.d.ts.map +1 -1
  5. package/dist/cli/commands/add-flow.js +2 -0
  6. package/dist/cli/commands/add-flow.js.map +1 -1
  7. package/dist/cli/commands/add.d.ts.map +1 -1
  8. package/dist/cli/commands/add.js +4 -0
  9. package/dist/cli/commands/add.js.map +1 -1
  10. package/dist/cli/commands/capability.d.ts.map +1 -1
  11. package/dist/cli/commands/capability.js +61 -0
  12. package/dist/cli/commands/capability.js.map +1 -1
  13. package/dist/cli/commands/delivery.d.ts.map +1 -1
  14. package/dist/cli/commands/delivery.js +89 -2
  15. package/dist/cli/commands/delivery.js.map +1 -1
  16. package/dist/cli/commands/eval.js +3 -3
  17. package/dist/cli/commands/eval.js.map +1 -1
  18. package/dist/cli/commands/gate.d.ts +3 -0
  19. package/dist/cli/commands/gate.d.ts.map +1 -0
  20. package/dist/cli/commands/gate.js +83 -0
  21. package/dist/cli/commands/gate.js.map +1 -0
  22. package/dist/cli/commands/generate.d.ts.map +1 -1
  23. package/dist/cli/commands/generate.js +2 -1
  24. package/dist/cli/commands/generate.js.map +1 -1
  25. package/dist/cli/commands/init.d.ts.map +1 -1
  26. package/dist/cli/commands/init.js +32 -2
  27. package/dist/cli/commands/init.js.map +1 -1
  28. package/dist/cli/commands/journey.d.ts +3 -0
  29. package/dist/cli/commands/journey.d.ts.map +1 -0
  30. package/dist/cli/commands/journey.js +114 -0
  31. package/dist/cli/commands/journey.js.map +1 -0
  32. package/dist/cli/commands/update.d.ts.map +1 -1
  33. package/dist/cli/commands/update.js +10 -3
  34. package/dist/cli/commands/update.js.map +1 -1
  35. package/dist/cli/index.js +4 -0
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
  38. package/dist/dashboard/snapshot-builder.js +10 -1
  39. package/dist/dashboard/snapshot-builder.js.map +1 -1
  40. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  41. package/dist/exporters/csv-exporter.js +3 -1
  42. package/dist/exporters/csv-exporter.js.map +1 -1
  43. package/dist/exporters/json-exporter.js +3 -1
  44. package/dist/exporters/json-exporter.js.map +1 -1
  45. package/dist/exporters/mobile-app-id.d.ts +22 -0
  46. package/dist/exporters/mobile-app-id.d.ts.map +1 -0
  47. package/dist/exporters/mobile-app-id.js +37 -0
  48. package/dist/exporters/mobile-app-id.js.map +1 -0
  49. package/dist/exporters/spec-parser.d.ts.map +1 -1
  50. package/dist/exporters/spec-parser.js +6 -2
  51. package/dist/exporters/spec-parser.js.map +1 -1
  52. package/dist/generators/test-generator/adapters/adapter-interface.d.ts +3 -0
  53. package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
  54. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +7 -4
  55. package/dist/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +20 -3
  56. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +4 -1
  57. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +4 -1
  58. package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +3 -0
  59. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  60. package/dist/generators/test-generator/code-generator.js +48 -9
  61. package/dist/generators/test-generator/code-generator.js.map +1 -1
  62. package/dist/generators/test-generator/template-engine.d.ts +3 -0
  63. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  64. package/dist/generators/test-generator/template-engine.js +11 -1
  65. package/dist/generators/test-generator/template-engine.js.map +1 -1
  66. package/dist/generators/test-generator/utils/selector-resolver.d.ts +11 -1
  67. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  68. package/dist/generators/test-generator/utils/selector-resolver.js +33 -0
  69. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  70. package/dist/harness/audit.d.ts.map +1 -1
  71. package/dist/harness/audit.js +8 -1
  72. package/dist/harness/audit.js.map +1 -1
  73. package/dist/harness/catalog/universal-viewpoints.yaml +15 -0
  74. package/dist/harness/eval/skill-lint.d.ts +3 -3
  75. package/dist/harness/eval/skill-lint.d.ts.map +1 -1
  76. package/dist/harness/eval/skill-lint.js +88 -68
  77. package/dist/harness/eval/skill-lint.js.map +1 -1
  78. package/dist/harness/journey.d.ts +88 -0
  79. package/dist/harness/journey.d.ts.map +1 -0
  80. package/dist/harness/journey.js +414 -0
  81. package/dist/harness/journey.js.map +1 -0
  82. package/dist/harness/quality-gates.d.ts +13 -0
  83. package/dist/harness/quality-gates.d.ts.map +1 -1
  84. package/dist/harness/quality-gates.js +23 -0
  85. package/dist/harness/quality-gates.js.map +1 -1
  86. package/dist/harness/sensors.d.ts +1 -1
  87. package/dist/harness/sensors.d.ts.map +1 -1
  88. package/dist/harness/sensors.js +32 -9
  89. package/dist/harness/sensors.js.map +1 -1
  90. package/dist/orchestrator/ai-rules-updater.d.ts +11 -3
  91. package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
  92. package/dist/orchestrator/ai-rules-updater.js +44 -180
  93. package/dist/orchestrator/ai-rules-updater.js.map +1 -1
  94. package/dist/orchestrator/ai-skills/adapters.d.ts +28 -0
  95. package/dist/orchestrator/ai-skills/adapters.d.ts.map +1 -0
  96. package/dist/orchestrator/ai-skills/adapters.js +34 -0
  97. package/dist/orchestrator/ai-skills/adapters.js.map +1 -0
  98. package/dist/orchestrator/ai-skills/build.d.ts +10 -0
  99. package/dist/orchestrator/ai-skills/build.d.ts.map +1 -0
  100. package/dist/orchestrator/ai-skills/build.js +128 -0
  101. package/dist/orchestrator/ai-skills/build.js.map +1 -0
  102. package/dist/orchestrator/ai-skills/cap-stripper.d.ts +27 -0
  103. package/dist/orchestrator/ai-skills/cap-stripper.d.ts.map +1 -0
  104. package/dist/orchestrator/ai-skills/cap-stripper.js +78 -0
  105. package/dist/orchestrator/ai-skills/cap-stripper.js.map +1 -0
  106. package/dist/orchestrator/ai-skills/commands.d.ts +22 -0
  107. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -0
  108. package/dist/orchestrator/ai-skills/commands.js +109 -0
  109. package/dist/orchestrator/ai-skills/commands.js.map +1 -0
  110. package/dist/orchestrator/ai-skills/generator.d.ts +23 -0
  111. package/dist/orchestrator/ai-skills/generator.d.ts.map +1 -0
  112. package/dist/orchestrator/ai-skills/generator.js +79 -0
  113. package/dist/orchestrator/ai-skills/generator.js.map +1 -0
  114. package/dist/orchestrator/ai-skills/golden-skills.test.d.ts +2 -0
  115. package/dist/orchestrator/ai-skills/golden-skills.test.d.ts.map +1 -0
  116. package/dist/orchestrator/ai-skills/golden-skills.test.js +183 -0
  117. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -0
  118. package/dist/orchestrator/ai-skills/resolve-assistants.d.ts +8 -0
  119. package/dist/orchestrator/ai-skills/resolve-assistants.d.ts.map +1 -0
  120. package/dist/orchestrator/ai-skills/resolve-assistants.js +28 -0
  121. package/dist/orchestrator/ai-skills/resolve-assistants.js.map +1 -0
  122. package/dist/orchestrator/ai-skills/writer.d.ts +15 -0
  123. package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -0
  124. package/dist/orchestrator/ai-skills/writer.js +129 -0
  125. package/dist/orchestrator/ai-skills/writer.js.map +1 -0
  126. package/dist/orchestrator/flow-manager.d.ts +11 -0
  127. package/dist/orchestrator/flow-manager.d.ts.map +1 -1
  128. package/dist/orchestrator/flow-manager.js +147 -29
  129. package/dist/orchestrator/flow-manager.js.map +1 -1
  130. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts +59 -0
  131. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -0
  132. package/dist/orchestrator/mobile-runtime-scaffolder.js +298 -0
  133. package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -0
  134. package/dist/orchestrator/project-initializer.d.ts +44 -7
  135. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  136. package/dist/orchestrator/project-initializer.js +147 -79
  137. package/dist/orchestrator/project-initializer.js.map +1 -1
  138. package/dist/orchestrator/screen-manager.d.ts +30 -0
  139. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  140. package/dist/orchestrator/screen-manager.js +205 -22
  141. package/dist/orchestrator/screen-manager.js.map +1 -1
  142. package/dist/orchestrator/templates/ai-src/README.md +58 -0
  143. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-add-flow.md → ai-src/commands/add-flow.md} +63 -2
  144. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-add-screen.md → ai-src/commands/add-screen.md} +171 -6
  145. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-create-test.md → ai-src/commands/create-test.md} +118 -4
  146. package/{src/orchestrator/templates/ai-instructions/claude-cmd-dashboard.md → dist/orchestrator/templates/ai-src/commands/dashboard.md} +25 -3
  147. package/{src/orchestrator/templates/ai-instructions/claude-cmd-delivery.md → dist/orchestrator/templates/ai-src/commands/delivery.md} +67 -3
  148. package/dist/orchestrator/templates/ai-src/commands/design.md +29 -0
  149. package/{src/orchestrator/templates/ai-instructions/claude-cmd-feedback.md → dist/orchestrator/templates/ai-src/commands/feedback.md} +34 -3
  150. package/dist/orchestrator/templates/{ai-instructions/claude-cmd-locale.md → ai-src/commands/locale.md} +56 -4
  151. package/{src/orchestrator/templates/ai-instructions/claude-cmd-review.md → dist/orchestrator/templates/ai-src/commands/review.md} +57 -2
  152. package/{src/orchestrator/templates/ai-instructions/claude-cmd-run-test.md → dist/orchestrator/templates/ai-src/commands/run-test.md} +116 -5
  153. package/dist/orchestrator/templates/ai-src/config/codex.md +8 -0
  154. package/{src/orchestrator/templates/ai-instructions/claude-skill-capture-mobile.md → dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md} +11 -0
  155. package/dist/orchestrator/templates/{ai-instructions/github-skill-sungen-error-mapping.md → ai-src/skills/sungen-error-mapping/SKILL.md} +49 -9
  156. package/{src/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md → dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md} +5 -5
  157. package/{src/orchestrator/templates/ai-instructions/claude-skill-locale.md → dist/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md} +275 -3
  158. package/{src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md → dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md} +17 -0
  159. package/dist/orchestrator/templates/{ai-instructions/claude-skill-selector-fix-mobile.md → ai-src/skills/sungen-selector-fix-mobile/SKILL.md} +5 -0
  160. package/dist/orchestrator/templates/{ai-instructions/claude-skill-tc-generation.md → ai-src/skills/sungen-tc-generation/SKILL.md} +5 -0
  161. package/dist/orchestrator/templates/playwright.config.ts +7 -4
  162. package/package.json +3 -3
  163. package/src/capabilities/discover.ts +29 -3
  164. package/src/cli/commands/add-flow.ts +2 -0
  165. package/src/cli/commands/add.ts +4 -0
  166. package/src/cli/commands/capability.ts +64 -1
  167. package/src/cli/commands/delivery.ts +89 -2
  168. package/src/cli/commands/eval.ts +3 -3
  169. package/src/cli/commands/gate.ts +44 -0
  170. package/src/cli/commands/generate.ts +2 -1
  171. package/src/cli/commands/init.ts +50 -3
  172. package/src/cli/commands/journey.ts +72 -0
  173. package/src/cli/commands/update.ts +14 -4
  174. package/src/cli/index.ts +4 -0
  175. package/src/dashboard/snapshot-builder.ts +10 -1
  176. package/src/exporters/csv-exporter.ts +3 -1
  177. package/src/exporters/json-exporter.ts +3 -1
  178. package/src/exporters/mobile-app-id.ts +34 -0
  179. package/src/exporters/spec-parser.ts +6 -2
  180. package/src/generators/test-generator/adapters/adapter-interface.ts +3 -0
  181. package/src/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +7 -4
  182. package/src/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +20 -3
  183. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +4 -1
  184. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +4 -1
  185. package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +3 -0
  186. package/src/generators/test-generator/code-generator.ts +49 -9
  187. package/src/generators/test-generator/template-engine.ts +14 -1
  188. package/src/generators/test-generator/utils/selector-resolver.ts +51 -1
  189. package/src/harness/audit.ts +9 -2
  190. package/src/harness/catalog/universal-viewpoints.yaml +15 -0
  191. package/src/harness/eval/skill-lint.ts +71 -60
  192. package/src/harness/journey.ts +434 -0
  193. package/src/harness/quality-gates.ts +20 -0
  194. package/src/harness/sensors.ts +25 -8
  195. package/src/orchestrator/ai-rules-updater.ts +35 -197
  196. package/src/orchestrator/ai-skills/adapters.ts +52 -0
  197. package/src/orchestrator/ai-skills/build.ts +103 -0
  198. package/src/orchestrator/ai-skills/cap-stripper.ts +85 -0
  199. package/src/orchestrator/ai-skills/commands.ts +112 -0
  200. package/src/orchestrator/ai-skills/generator.ts +47 -0
  201. package/src/orchestrator/ai-skills/golden-skills.test.ts +124 -0
  202. package/src/orchestrator/ai-skills/resolve-assistants.ts +18 -0
  203. package/src/orchestrator/ai-skills/writer.ts +92 -0
  204. package/src/orchestrator/flow-manager.ts +165 -29
  205. package/src/orchestrator/mobile-runtime-scaffolder.ts +277 -0
  206. package/src/orchestrator/project-initializer.ts +159 -86
  207. package/src/orchestrator/screen-manager.ts +237 -22
  208. package/src/orchestrator/templates/ai-src/README.md +58 -0
  209. package/src/orchestrator/templates/{ai-instructions/claude-cmd-add-flow.md → ai-src/commands/add-flow.md} +63 -2
  210. package/src/orchestrator/templates/{ai-instructions/claude-cmd-add-screen.md → ai-src/commands/add-screen.md} +171 -6
  211. package/src/orchestrator/templates/{ai-instructions/claude-cmd-create-test.md → ai-src/commands/create-test.md} +118 -4
  212. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-dashboard.md → src/orchestrator/templates/ai-src/commands/dashboard.md} +25 -3
  213. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-delivery.md → src/orchestrator/templates/ai-src/commands/delivery.md} +67 -3
  214. package/src/orchestrator/templates/ai-src/commands/design.md +29 -0
  215. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-feedback.md → src/orchestrator/templates/ai-src/commands/feedback.md} +34 -3
  216. package/src/orchestrator/templates/{ai-instructions/claude-cmd-locale.md → ai-src/commands/locale.md} +56 -4
  217. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-review.md → src/orchestrator/templates/ai-src/commands/review.md} +57 -2
  218. package/{dist/orchestrator/templates/ai-instructions/claude-cmd-run-test.md → src/orchestrator/templates/ai-src/commands/run-test.md} +116 -5
  219. package/src/orchestrator/templates/ai-src/config/codex.md +8 -0
  220. package/{dist/orchestrator/templates/ai-instructions/claude-skill-capture-mobile.md → src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md} +11 -0
  221. package/{dist/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md → src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md} +49 -9
  222. package/{dist/orchestrator/templates/ai-instructions/claude-skill-harness-audit.md → src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md} +5 -5
  223. package/{dist/orchestrator/templates/ai-instructions/claude-skill-locale.md → src/orchestrator/templates/ai-src/skills/sungen-locale/SKILL.md} +275 -3
  224. package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix.md → src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md} +17 -0
  225. package/src/orchestrator/templates/{ai-instructions/claude-skill-selector-fix-mobile.md → ai-src/skills/sungen-selector-fix-mobile/SKILL.md} +5 -0
  226. package/src/orchestrator/templates/{ai-instructions/claude-skill-tc-generation.md → ai-src/skills/sungen-tc-generation/SKILL.md} +5 -0
  227. package/src/orchestrator/templates/playwright.config.ts +7 -4
  228. package/dist/orchestrator/templates/ai-instructions/claude-cmd-design.md +0 -12
  229. package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +0 -347
  230. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +0 -86
  231. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +0 -131
  232. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +0 -93
  233. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-dashboard.md +0 -65
  234. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +0 -83
  235. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-design.md +0 -13
  236. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +0 -24
  237. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-locale.md +0 -70
  238. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-review.md +0 -38
  239. package/dist/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +0 -155
  240. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mobile.md +0 -184
  241. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +0 -85
  242. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-locale.md +0 -291
  243. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix-mobile.md +0 -316
  244. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +0 -221
  245. package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +0 -617
  246. package/src/orchestrator/templates/ai-instructions/claude-cmd-design.md +0 -12
  247. package/src/orchestrator/templates/ai-instructions/claude-skill-api-design.md +0 -62
  248. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-mcp.md +0 -82
  249. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-figma-pat.md +0 -117
  250. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-live.md +0 -60
  251. package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mode-local.md +0 -38
  252. package/src/orchestrator/templates/ai-instructions/claude-skill-capture.md +0 -35
  253. package/src/orchestrator/templates/ai-instructions/claude-skill-dashboard.md +0 -121
  254. package/src/orchestrator/templates/ai-instructions/claude-skill-delivery.md +0 -141
  255. package/src/orchestrator/templates/ai-instructions/claude-skill-error-mapping.md +0 -157
  256. package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +0 -446
  257. package/src/orchestrator/templates/ai-instructions/claude-skill-ingest-legacy.md +0 -79
  258. package/src/orchestrator/templates/ai-instructions/claude-skill-mobile-gestures.md +0 -109
  259. package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix.md +0 -347
  260. package/src/orchestrator/templates/ai-instructions/claude-skill-tc-review.md +0 -320
  261. package/src/orchestrator/templates/ai-instructions/claude-skill-test-design-techniques.md +0 -118
  262. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-a-data-entry.md +0 -203
  263. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-b-data-ops.md +0 -179
  264. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-c-data-explore.md +0 -233
  265. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-d-display.md +0 -226
  266. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint-group-e-identity.md +0 -177
  267. package/src/orchestrator/templates/ai-instructions/claude-skill-viewpoint.md +0 -118
  268. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-flow.md +0 -86
  269. package/src/orchestrator/templates/ai-instructions/copilot-cmd-add-screen.md +0 -131
  270. package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +0 -93
  271. package/src/orchestrator/templates/ai-instructions/copilot-cmd-dashboard.md +0 -65
  272. package/src/orchestrator/templates/ai-instructions/copilot-cmd-delivery.md +0 -83
  273. package/src/orchestrator/templates/ai-instructions/copilot-cmd-design.md +0 -13
  274. package/src/orchestrator/templates/ai-instructions/copilot-cmd-feedback.md +0 -24
  275. package/src/orchestrator/templates/ai-instructions/copilot-cmd-locale.md +0 -70
  276. package/src/orchestrator/templates/ai-instructions/copilot-cmd-review.md +0 -38
  277. package/src/orchestrator/templates/ai-instructions/copilot-cmd-run-test.md +0 -155
  278. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-api-design.md +0 -62
  279. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mobile.md +0 -184
  280. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md +0 -82
  281. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md +0 -117
  282. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md +0 -60
  283. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md +0 -38
  284. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md +0 -35
  285. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-dashboard.md +0 -121
  286. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +0 -141
  287. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-error-mapping.md +0 -157
  288. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +0 -446
  289. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-harness-audit.md +0 -85
  290. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-ingest-legacy.md +0 -79
  291. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-locale.md +0 -291
  292. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-mobile-gestures.md +0 -109
  293. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix-mobile.md +0 -316
  294. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-keys.md +0 -221
  295. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +0 -617
  296. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md +0 -320
  297. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-test-design-techniques.md +0 -118
  298. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-a-data-entry.md +0 -203
  299. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-b-data-ops.md +0 -179
  300. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-c-data-explore.md +0 -233
  301. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-d-display.md +0 -226
  302. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-e-identity.md +0 -177
  303. package/src/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md +0 -118
  304. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-challenge.md → ai-src/agents/challenge.md} +0 -0
  305. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-discovery.md → ai-src/agents/discovery.md} +0 -0
  306. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-generator.md → ai-src/agents/generator.md} +0 -0
  307. /package/dist/orchestrator/templates/{ai-instructions/claude-agent-reviewer.md → ai-src/agents/reviewer.md} +0 -0
  308. /package/dist/orchestrator/templates/{ai-instructions/claude-config.md → ai-src/config/claude.md} +0 -0
  309. /package/dist/orchestrator/templates/{ai-instructions/copilot-config.md → ai-src/config/copilot.md} +0 -0
  310. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-api-design.md → ai-src/skills/sungen-api-design/SKILL.md} +0 -0
  311. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture.md → ai-src/skills/sungen-capture/SKILL.md} +0 -0
  312. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-figma-mcp.md → ai-src/skills/sungen-capture/mode-figma-mcp.md} +0 -0
  313. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-figma-pat.md → ai-src/skills/sungen-capture/mode-figma-pat.md} +0 -0
  314. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-live.md → ai-src/skills/sungen-capture/mode-live.md} +0 -0
  315. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-capture-mode-local.md → ai-src/skills/sungen-capture/mode-local.md} +0 -0
  316. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-dashboard.md → ai-src/skills/sungen-dashboard/SKILL.md} +0 -0
  317. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-delivery.md → ai-src/skills/sungen-delivery/SKILL.md} +0 -0
  318. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-gherkin-syntax.md → ai-src/skills/sungen-gherkin-syntax/SKILL.md} +0 -0
  319. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-ingest-legacy.md → ai-src/skills/sungen-ingest-legacy/SKILL.md} +0 -0
  320. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-mobile-gestures.md → ai-src/skills/sungen-mobile-gestures/SKILL.md} +0 -0
  321. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-selector-keys.md → ai-src/skills/sungen-selector-keys/SKILL.md} +0 -0
  322. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-tc-review.md → ai-src/skills/sungen-tc-review/SKILL.md} +0 -0
  323. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-test-design-techniques.md → ai-src/skills/sungen-test-design-techniques/SKILL.md} +0 -0
  324. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint.md → ai-src/skills/sungen-viewpoint/SKILL.md} +0 -0
  325. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-a-data-entry.md → ai-src/skills/sungen-viewpoint/group-a-data-entry.md} +0 -0
  326. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-b-data-ops.md → ai-src/skills/sungen-viewpoint/group-b-data-ops.md} +0 -0
  327. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-c-data-explore.md → ai-src/skills/sungen-viewpoint/group-c-data-explore.md} +0 -0
  328. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-d-display.md → ai-src/skills/sungen-viewpoint/group-d-display.md} +0 -0
  329. /package/dist/orchestrator/templates/{ai-instructions/claude-skill-viewpoint-group-e-identity.md → ai-src/skills/sungen-viewpoint/group-e-identity.md} +0 -0
  330. /package/src/orchestrator/templates/{ai-instructions/claude-agent-challenge.md → ai-src/agents/challenge.md} +0 -0
  331. /package/src/orchestrator/templates/{ai-instructions/claude-agent-discovery.md → ai-src/agents/discovery.md} +0 -0
  332. /package/src/orchestrator/templates/{ai-instructions/claude-agent-generator.md → ai-src/agents/generator.md} +0 -0
  333. /package/src/orchestrator/templates/{ai-instructions/claude-agent-reviewer.md → ai-src/agents/reviewer.md} +0 -0
  334. /package/src/orchestrator/templates/{ai-instructions/claude-config.md → ai-src/config/claude.md} +0 -0
  335. /package/src/orchestrator/templates/{ai-instructions/copilot-config.md → ai-src/config/copilot.md} +0 -0
  336. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-api-design.md → src/orchestrator/templates/ai-src/skills/sungen-api-design/SKILL.md} +0 -0
  337. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture.md → src/orchestrator/templates/ai-src/skills/sungen-capture/SKILL.md} +0 -0
  338. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-mcp.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-figma-mcp.md} +0 -0
  339. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-figma-pat.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-figma-pat.md} +0 -0
  340. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-live.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-live.md} +0 -0
  341. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mode-local.md → src/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md} +0 -0
  342. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-dashboard.md → src/orchestrator/templates/ai-src/skills/sungen-dashboard/SKILL.md} +0 -0
  343. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md → src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md} +0 -0
  344. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md → src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md} +0 -0
  345. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-ingest-legacy.md → src/orchestrator/templates/ai-src/skills/sungen-ingest-legacy/SKILL.md} +0 -0
  346. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-mobile-gestures.md → src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md} +0 -0
  347. /package/src/orchestrator/templates/{ai-instructions/claude-skill-selector-keys.md → ai-src/skills/sungen-selector-keys/SKILL.md} +0 -0
  348. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-review.md → src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md} +0 -0
  349. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-test-design-techniques.md → src/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md} +0 -0
  350. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md} +0 -0
  351. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-a-data-entry.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-a-data-entry.md} +0 -0
  352. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-b-data-ops.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-b-data-ops.md} +0 -0
  353. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-c-data-explore.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-c-data-explore.md} +0 -0
  354. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-d-display.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-d-display.md} +0 -0
  355. /package/{dist/orchestrator/templates/ai-instructions/github-skill-sungen-viewpoint-group-e-identity.md → src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-e-identity.md} +0 -0
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Journey board (epic #381, story S1) — the durable, read-only "you are here" view.
3
+ *
4
+ * Reconstructs the QA's position from artifacts ALREADY on disk (no recomputation, no context
5
+ * needed): the audit report (`.sungen/reports/<slug>-audit.json`) supplies the obligation status
6
+ * via its calibration axes + gate + findings; the ledger (`.sungen/ledger/<slug>.jsonl`) supplies
7
+ * the phase history ("you are here"). The output answers the three QA questions — what's next /
8
+ * what to review / what's doubtful — and persists `.sungen/journey/<slug>.{json,board.md}`.
9
+ *
10
+ * S1 = the read-only synthesis. S2 (this file) adds the **writable lifecycle**: persisted
11
+ * waivers (reason-required, anti-amnesia), reconcile (auto-close satisfied; re-surface a waiver
12
+ * when its evidence changed), via `runJourney` + `waive`. Gate-bound predicates + inter-phase
13
+ * gates are S3. Pure-deterministic, no AI.
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import * as crypto from 'crypto';
18
+ import { reportSlug } from './unit-paths';
19
+
20
+ export type ObStatus = 'satisfied' | 'needs-work' | 'pending' | 'waived';
21
+
22
+ export interface Obligation {
23
+ id: string;
24
+ title: string;
25
+ status: ObStatus;
26
+ detail: string;
27
+ waivedReason?: string; // S2 — set when the QA explicitly waived this obligation
28
+ }
29
+
30
+ /** S5 — a parsed anchor so an IDE can jump straight to what needs review. */
31
+ export interface Anchor { vpId?: string; file?: string; line?: number; }
32
+ export interface ReviewItem { text: string; anchor: Anchor; }
33
+
34
+ export interface JourneyReport {
35
+ unit: string;
36
+ generatedFrom: string[]; // which artifacts were available
37
+ youAreHere: string; // last ledger phase, or 'not started'
38
+ phasesDone: string[];
39
+ gateStatus: string | null;
40
+ score: number | null;
41
+ obligations: Obligation[];
42
+ needsYou: ReviewItem[]; // S5 — the review queue: human-judgment findings, each with an anchor
43
+ nextSuggested: string;
44
+ }
45
+
46
+ function readJSON(p: string): any {
47
+ try { return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf-8')) : null; } catch { return null; }
48
+ }
49
+
50
+ function readLedgerPhases(p: string): string[] {
51
+ if (!fs.existsSync(p)) return [];
52
+ const out: string[] = [];
53
+ for (const line of fs.readFileSync(p, 'utf-8').split('\n')) {
54
+ if (!line.trim()) continue;
55
+ try { const d = JSON.parse(line); if (d.step) out.push(String(d.step)); } catch { /* skip */ }
56
+ }
57
+ return out;
58
+ }
59
+
60
+ /** Findings that ask for HUMAN judgment (the review-queue seed), vs pure machine-repair targets. */
61
+ function isHumanFinding(f: string): boolean {
62
+ return /@manual|MANUAL-|DEPTH-DEFERRED|UNSOURCEABLE|CAPABILITY-SUGGESTION|judgment|oracle|review/i.test(f);
63
+ }
64
+
65
+ /** S5 — pull an IDE-jumpable anchor out of a finding (VP id, scenario, or `file.ext:line`). */
66
+ function extractAnchor(text: string): Anchor {
67
+ const vp = text.match(/\bVP-[A-Z0-9-]+\b/);
68
+ const fl = text.match(/([\w./-]+\.(?:md|feature|ts|yaml)):(\d+)/);
69
+ return { vpId: vp ? vp[0] : undefined, file: fl ? fl[1] : undefined, line: fl ? Number(fl[2]) : undefined };
70
+ }
71
+
72
+ const SAT = 0.8; // axis at/above this = satisfied (below = needs-work)
73
+
74
+ function computeFresh(projectRoot: string, unit: string): JourneyReport {
75
+ const slug = reportSlug(unit);
76
+ const audit = readJSON(path.join(projectRoot, '.sungen', 'reports', `${slug}-audit.json`));
77
+ const phases = readLedgerPhases(path.join(projectRoot, '.sungen', 'ledger', `${slug}.jsonl`));
78
+
79
+ const from: string[] = [];
80
+ if (audit) from.push('audit');
81
+ if (phases.length) from.push('ledger');
82
+
83
+ const youAreHere = phases.length ? phases[phases.length - 1] : 'not started';
84
+ const ran = (p: string) => phases.some((s) => s === p || s.startsWith(p));
85
+
86
+ const obligations: Obligation[] = [];
87
+ const needsYou: ReviewItem[] = [];
88
+
89
+ if (!audit) {
90
+ // Nothing measured yet — the journey hasn't really begun.
91
+ obligations.push({ id: 'OB-create', title: 'Generate test cases', status: 'pending', detail: 'No audit report yet — run /sungen:create-test.' });
92
+ return {
93
+ unit, generatedFrom: from, youAreHere, phasesDone: phases, gateStatus: null, score: null,
94
+ obligations, needsYou, nextSuggested: 'Run `/sungen:create-test ' + unit + '` to begin.',
95
+ };
96
+ }
97
+
98
+ const ax = (audit.calibration && audit.calibration.axes) || {};
99
+ const depthThreshold = (audit.depth && audit.depth.threshold) || SAT;
100
+ const ob = (id: string, title: string, val: number | undefined, thr: number, detail: string): Obligation => ({
101
+ id, title,
102
+ status: val === undefined ? 'pending' : val >= thr ? 'satisfied' : 'needs-work',
103
+ detail: val === undefined ? detail : `${Math.round(val * 100)}% (need ${Math.round(thr * 100)}%) — ${detail}`,
104
+ });
105
+
106
+ obligations.push(ob('OB-spec', 'Spec readiness', ax.specFR, 1, 'FR/sections sufficient to design from'));
107
+ obligations.push(ob('OB-coverage', 'Viewpoint coverage', ax.coverage, SAT, 'critical themes covered'));
108
+ obligations.push(ob('OB-depth', 'Assertion depth', ax.businessDepth, depthThreshold, 'business-critical scenarios assert data'));
109
+ obligations.push(ob('OB-trace', 'Traceability', ax.taxonomy ?? ax.traceability, SAT, 'scenarios trace to viewpoints'));
110
+
111
+ // Automation obligation: automatable @manual still pending a driver = needs-work.
112
+ const autoPending = audit.automatableManual && audit.automatableManual.automatable > 0;
113
+ obligations.push({
114
+ id: 'OB-automation', title: 'Automation coverage',
115
+ status: autoPending ? 'needs-work' : 'satisfied',
116
+ detail: autoPending ? `${audit.automatableManual.automatable} @manual scenario(s) a driver could automate` : 'no capability-pending automatable cases',
117
+ });
118
+
119
+ // Needs-you: the review queue. Prefer PRECISE per-scenario anchors from audit's structured
120
+ // depth data — each shallow business-critical scenario carries its own VP id — over regex-parsing
121
+ // the aggregate "DEPTH:" finding string (which names no scenario, so points nowhere). Then
122
+ // supplement with the remaining human-judgment findings.
123
+ const shallowBC: { name: string; category?: string }[] = (audit.depth && audit.depth.shallowBusinessCritical) || [];
124
+ for (const s of shallowBC) {
125
+ needsYou.push({
126
+ text: `DEEPEN — "${s.name}": assert data, not just visibility (\`… with {{value}}\` / \`see all … contain {{v}}\`), or defer cross-screen with @manual + reason.`,
127
+ anchor: extractAnchor(s.name),
128
+ });
129
+ }
130
+ const haveStructuredDepth = shallowBC.length > 0;
131
+ for (const f of (audit.findings || [])) {
132
+ if (!isHumanFinding(f)) continue;
133
+ if (haveStructuredDepth && /^\s*DEPTH\b/i.test(f)) continue; // superseded by the per-scenario items above
134
+ needsYou.push({ text: f, anchor: extractAnchor(f) });
135
+ }
136
+
137
+ // Human sign-off: always pending until S5 explicit signoff; carries the now-final review-queue count.
138
+ const manualCount = (audit.manualOracle && (audit.manualOracle.manualTotal ?? audit.manualOracle.total)) || 0;
139
+ obligations.push({
140
+ id: 'OB-signoff', title: 'Human sign-off', status: 'pending',
141
+ detail: `${needsYou.length} item(s) queued for your review · ${manualCount} @manual scenario(s)`,
142
+ });
143
+
144
+ // Next suggested = first non-satisfied obligation; if all satisfied but run-test not done → run.
145
+ const firstGap = obligations.find((o) => o.status !== 'satisfied' && o.id !== 'OB-signoff');
146
+ let nextSuggested: string;
147
+ if (firstGap) nextSuggested = `Repair "${firstGap.title}" (${firstGap.detail}).`;
148
+ else if (!ran('run')) nextSuggested = `Quality satisfied — run \`/sungen:run-test ${unit}\`.`;
149
+ else nextSuggested = `All obligations satisfied — review the ${needsYou.length} queued item(s), then sign off & deliver.`;
150
+
151
+ return {
152
+ unit, generatedFrom: from, youAreHere, phasesDone: phases,
153
+ gateStatus: audit.gateStatus ?? null, score: (audit.score && audit.score.overall) ?? null,
154
+ obligations, needsYou, nextSuggested,
155
+ };
156
+ }
157
+
158
+ // ---------------- S2: writable lifecycle — persisted waivers + reconcile ----------------
159
+
160
+ interface Waiver { reason: string; at: string; auditHashAtWaive: string; }
161
+ interface Signoff { by?: string; at: string; auditHashAtSignoff: string; } // S5
162
+ interface JourneyState { unit: string; auditHash: string; waivers: Record<string, Waiver>; signoff?: Signoff; }
163
+
164
+ function statePath(projectRoot: string, slug: string): string {
165
+ return path.join(projectRoot, '.sungen', 'journey', `${slug}.state.json`);
166
+ }
167
+ /** Evidence cursor: the audit report's content hash. A waiver is invalidated when this changes. */
168
+ function auditHashOf(projectRoot: string, slug: string): string {
169
+ const p = path.join(projectRoot, '.sungen', 'reports', `${slug}-audit.json`);
170
+ return fs.existsSync(p) ? crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex') : '';
171
+ }
172
+ function loadState(p: string): JourneyState | null { return readJSON(p); }
173
+ function saveState(p: string, s: JourneyState): void {
174
+ fs.mkdirSync(path.dirname(p), { recursive: true });
175
+ fs.writeFileSync(p, JSON.stringify(s, null, 2), 'utf-8');
176
+ }
177
+
178
+ /** Recompute nextSuggested AFTER waivers are applied (a waived obligation is not a gap). */
179
+ function computeNext(r: JourneyReport, unit: string): string {
180
+ const gap = r.obligations.find((o) => o.status !== 'satisfied' && o.status !== 'waived' && o.id !== 'OB-signoff');
181
+ if (gap) return `Repair "${gap.title}" (${gap.detail}).`;
182
+ if (!r.phasesDone.some((s) => s === 'run' || s.startsWith('run'))) return `Quality satisfied — run \`/sungen:run-test ${unit}\`.`;
183
+ return `All obligations satisfied/waived — review the ${r.needsYou.length} queued item(s), then sign off & deliver.`;
184
+ }
185
+
186
+ /**
187
+ * The public entry: compute fresh, then RECONCILE with the persisted state —
188
+ * - auto-close is automatic (fresh recompute reflects the current artifacts);
189
+ * - an active waiver (evidence unchanged) sets status='waived' (carries the reason);
190
+ * - a STALE waiver (audit changed since it was waived) is re-surfaced for re-decision (anti-amnesia).
191
+ * Then persist the current evidence cursor.
192
+ */
193
+ export function runJourney(projectRoot: string, unit: string): JourneyReport {
194
+ const slug = reportSlug(unit);
195
+ const report = computeFresh(projectRoot, unit);
196
+ const sp = statePath(projectRoot, slug);
197
+ const state = loadState(sp) || { unit, auditHash: '', waivers: {} };
198
+ const curHash = auditHashOf(projectRoot, slug);
199
+
200
+ for (const ob of report.obligations) {
201
+ const w = state.waivers[ob.id];
202
+ if (!w) continue;
203
+ if (w.auditHashAtWaive === curHash) {
204
+ ob.status = 'waived';
205
+ ob.waivedReason = w.reason;
206
+ ob.detail = `waived — ${w.reason}`;
207
+ } else {
208
+ report.needsYou.unshift({ text: `⚠️ Waiver on "${ob.title}" is STALE (evidence changed since ${w.at}) — re-decide. Was: ${w.reason}`, anchor: { vpId: ob.id } });
209
+ }
210
+ }
211
+ // S5 — sign-off reconcile: a recorded sign-off satisfies OB-signoff while its evidence holds;
212
+ // if the audit changed since, the sign-off is STALE → re-surface for re-review (anti-amnesia).
213
+ const signoffOb = report.obligations.find((o) => o.id === 'OB-signoff');
214
+ if (signoffOb && state.signoff) {
215
+ if (state.signoff.auditHashAtSignoff === curHash) {
216
+ signoffOb.status = 'satisfied';
217
+ signoffOb.detail = `signed off${state.signoff.by ? ' by ' + state.signoff.by : ''} at ${state.signoff.at}`;
218
+ } else {
219
+ report.needsYou.unshift({ text: `⚠️ Sign-off is STALE (suite changed since ${state.signoff.at}) — re-review and sign off again.`, anchor: { vpId: 'OB-signoff' } });
220
+ }
221
+ }
222
+ report.nextSuggested = computeNext(report, unit);
223
+ saveState(sp, { unit, auditHash: curHash, waivers: state.waivers, signoff: state.signoff });
224
+ return report;
225
+ }
226
+
227
+ /**
228
+ * Waive an obligation — REQUIRES a reason (anti-amnesia: a waiver leaves a recorded "why").
229
+ * Records the current evidence cursor so reconcile can invalidate it if the audit changes.
230
+ */
231
+ export function waive(projectRoot: string, unit: string, obId: string, reason: string): JourneyReport {
232
+ if (!reason || !reason.trim()) {
233
+ throw new Error('A reason is required to waive (anti-amnesia: a waiver must record WHY). Use --reason "...".');
234
+ }
235
+ const slug = reportSlug(unit);
236
+ const fresh = computeFresh(projectRoot, unit);
237
+ const valid = fresh.obligations.map((o) => o.id);
238
+ if (!valid.includes(obId)) {
239
+ throw new Error(`Unknown obligation "${obId}". Valid: ${valid.join(', ')}`);
240
+ }
241
+ const sp = statePath(projectRoot, slug);
242
+ const state = loadState(sp) || { unit, auditHash: '', waivers: {} };
243
+ state.waivers[obId] = { reason: reason.trim(), at: new Date().toISOString(), auditHashAtWaive: auditHashOf(projectRoot, slug) };
244
+ saveState(sp, state);
245
+ return runJourney(projectRoot, unit);
246
+ }
247
+
248
+ /**
249
+ * Sign off — the single human gate (S5). Allowed ONLY when every other deliver-required
250
+ * obligation is satisfied or waived (you cannot sign off an unclean suite). Records who/when +
251
+ * the evidence cursor; reconcile invalidates it (stale → re-review) if the suite later changes.
252
+ */
253
+ export function signoff(projectRoot: string, unit: string, by?: string): JourneyReport {
254
+ const before = runJourney(projectRoot, unit);
255
+ const blockers = before.obligations.filter((o) => o.id !== 'OB-signoff' && o.status !== 'satisfied' && o.status !== 'waived');
256
+ if (blockers.length) {
257
+ throw new Error(`Cannot sign off — still open: ${blockers.map((b) => b.id).join(', ')}. Fix, or waive each with a reason, first.`);
258
+ }
259
+ const slug = reportSlug(unit);
260
+ const sp = statePath(projectRoot, slug);
261
+ const state = loadState(sp) || { unit, auditHash: '', waivers: {} };
262
+ state.signoff = { by, at: new Date().toISOString(), auditHashAtSignoff: auditHashOf(projectRoot, slug) };
263
+ saveState(sp, state);
264
+ return runJourney(projectRoot, unit);
265
+ }
266
+
267
+ const ICON: Record<ObStatus, string> = { satisfied: '✅', 'needs-work': '⚠️ ', pending: '⏳', waived: '🚫' };
268
+
269
+ export function renderJourneyBoard(r: JourneyReport): string {
270
+ const L: string[] = [];
271
+ L.push(`# Journey — ${r.unit}`);
272
+ L.push('');
273
+ L.push(`📍 **You are here:** ${r.youAreHere}` + (r.phasesDone.length ? ` (phases: ${r.phasesDone.join(' → ')})` : ''));
274
+ if (r.gateStatus) L.push(` gate: **${r.gateStatus}** · score: ${r.score ?? '—'}/10`);
275
+ L.push('');
276
+ L.push('## What must still be true');
277
+ for (const o of r.obligations) L.push(`- ${ICON[o.status]} **${o.title}** — ${o.detail}`);
278
+ L.push('');
279
+ L.push(`## 🔎 Review queue — needs your judgment (${r.needsYou.length})`);
280
+ if (r.needsYou.length) {
281
+ for (const it of r.needsYou.slice(0, 20)) {
282
+ const a = it.anchor;
283
+ const tag = [a.vpId, a.file && `${a.file}${a.line ? ':' + a.line : ''}`].filter(Boolean).join(' · ');
284
+ L.push(`- ${it.text}${tag ? ` 〔${tag}〕` : ''}`);
285
+ }
286
+ } else L.push('- (nothing queued)');
287
+ L.push('');
288
+ L.push(`## → Next`);
289
+ L.push(`${r.nextSuggested}`);
290
+ L.push('');
291
+ return L.join('\n');
292
+ }
293
+
294
+ // ---------------- S3: inter-phase gate — obligations as HALT predicates (#398) ----------------
295
+ //
296
+ // A phase boundary is a deterministic gate: before the next phase may run, this phase's required
297
+ // obligations must each be `satisfied` OR `waived` (S2 — an explicit, reasoned human acceptance).
298
+ // A required obligation still `needs-work`/`pending` is a BLOCKER → HALT (no silent bad output
299
+ // crosses the boundary, §9). Reuses runJourney → obligations already reflect waivers/reconcile.
300
+
301
+ export type GatePhase = 'create' | 'run' | 'deliver';
302
+
303
+ export interface GateVerdict {
304
+ unit: string;
305
+ phase: GatePhase;
306
+ status: 'pass' | 'halt';
307
+ required: string[];
308
+ blockers: { id: string; title: string; detail: string }[];
309
+ waivedCredit: { id: string; title: string }[]; // required obligations accepted via an explicit waiver
310
+ }
311
+
312
+ const PHASE_REQUIRED: Record<GatePhase, string[]> = {
313
+ // post-create (design quality): spec + coverage + depth + traceability must hold.
314
+ create: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace'],
315
+ // post-run: the design gates + automation coverage.
316
+ run: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace', 'OB-automation'],
317
+ // pre-delivery: everything automated PLUS the single human sign-off (S5).
318
+ deliver: ['OB-spec', 'OB-coverage', 'OB-depth', 'OB-trace', 'OB-automation', 'OB-signoff'],
319
+ };
320
+
321
+ export function runGate(projectRoot: string, unit: string, phase: GatePhase): GateVerdict {
322
+ const r = runJourney(projectRoot, unit);
323
+ const required = PHASE_REQUIRED[phase];
324
+ const reqObs = r.obligations.filter((o) => required.includes(o.id));
325
+ const blockers = reqObs
326
+ .filter((o) => o.status !== 'satisfied' && o.status !== 'waived')
327
+ .map((o) => ({ id: o.id, title: o.title, detail: o.detail }));
328
+ const waivedCredit = reqObs.filter((o) => o.status === 'waived').map((o) => ({ id: o.id, title: o.title }));
329
+ return { unit, phase, status: blockers.length ? 'halt' : 'pass', required, blockers, waivedCredit };
330
+ }
331
+
332
+ export function renderGate(v: GateVerdict): string {
333
+ const L: string[] = [];
334
+ L.push('');
335
+ L.push(`━━━ Gate: ${v.unit} @ phase "${v.phase}" → ${v.status === 'pass' ? '✅ PASS' : '⛔ HALT'} ━━━`);
336
+ if (v.blockers.length) {
337
+ L.push(' Blocking obligations (must be satisfied or explicitly waived):');
338
+ for (const b of v.blockers) L.push(` • ${b.id} ${b.title} — ${b.detail}`);
339
+ L.push(' → Self-correct (repair / run-test), or `sungen journey --screen ' + v.unit + ' --waive <OB> --reason "..."` if accepted.');
340
+ } else {
341
+ L.push(' All required obligations satisfied' + (v.waivedCredit.length ? ` (${v.waivedCredit.length} accepted via waiver)` : '') + '.');
342
+ }
343
+ L.push('');
344
+ return L.join('\n');
345
+ }
346
+
347
+ // ---------------- #409: project-level roll-up across all units ----------------
348
+ //
349
+ // Multi-unit fan-out PARALLELISES generation but human review/sign-off must stay SERIAL — you can't
350
+ // judge N boards at once. This roll-up is that serial surface: one row per unit (gate · score · phase
351
+ // · what it still needs), and a single "review this next" pointer. The per-unit board is unchanged;
352
+ // this only aggregates the same read-only `runJourney` synthesis.
353
+
354
+ export type UnitKind = 'screen' | 'flow' | 'api';
355
+ export interface UnitSummary {
356
+ unit: string;
357
+ kind: UnitKind;
358
+ score: number | null;
359
+ gateStatus: string | null;
360
+ youAreHere: string;
361
+ obligationsOpen: number; // non-satisfied, non-waived (excludes OB-signoff)
362
+ reviewQueue: number;
363
+ signedOff: boolean;
364
+ started: boolean; // has an audit report yet
365
+ }
366
+
367
+ /** Discover every unit under qa/screens, qa/flows, and qa/api top-level areas. */
368
+ export function listUnits(projectRoot: string): { unit: string; kind: UnitKind }[] {
369
+ const out: { unit: string; kind: UnitKind }[] = [];
370
+ const scan = (sub: string, kind: UnitKind) => {
371
+ const d = path.join(projectRoot, 'qa', sub);
372
+ if (!fs.existsSync(d)) return;
373
+ for (const e of fs.readdirSync(d, { withFileTypes: true })) {
374
+ // 'flows' under qa/api is a container for api-flows (a nested tier) — not a unit itself.
375
+ if (e.isDirectory() && !(sub === 'api' && e.name === 'flows')) out.push({ unit: e.name, kind });
376
+ }
377
+ };
378
+ scan('screens', 'screen');
379
+ scan('flows', 'flow');
380
+ scan('api', 'api');
381
+ return out;
382
+ }
383
+
384
+ /** Roll up every unit's journey into one table of summaries (serial review surface). */
385
+ export function rollupJourney(projectRoot: string): UnitSummary[] {
386
+ return listUnits(projectRoot).map(({ unit, kind }) => {
387
+ const r = runJourney(projectRoot, unit);
388
+ const signedOff = r.obligations.find((o) => o.id === 'OB-signoff')?.status === 'satisfied';
389
+ const obligationsOpen = r.obligations.filter(
390
+ (o) => o.id !== 'OB-signoff' && o.status !== 'satisfied' && o.status !== 'waived',
391
+ ).length;
392
+ return {
393
+ unit, kind, score: r.score, gateStatus: r.gateStatus, youAreHere: r.youAreHere,
394
+ obligationsOpen, reviewQueue: r.needsYou.length, signedOff,
395
+ started: r.generatedFrom.includes('audit'),
396
+ };
397
+ });
398
+ }
399
+
400
+ function unitStatusLabel(r: UnitSummary): string {
401
+ if (!r.started) return '◻️ not started';
402
+ if (r.signedOff) return '✅ signed off';
403
+ if (r.obligationsOpen > 0) return `⏳ ${r.obligationsOpen} obligation(s) open`;
404
+ if (r.reviewQueue > 0) return `🔎 review ${r.reviewQueue} item(s)`;
405
+ return '🟡 ready to sign off';
406
+ }
407
+
408
+ export function renderRollupBoard(rows: UnitSummary[]): string {
409
+ const L: string[] = [];
410
+ L.push('# Journey roll-up — all units');
411
+ L.push('');
412
+ if (!rows.length) {
413
+ L.push('No units found under `qa/screens`, `qa/flows`, or `qa/api`.');
414
+ return L.join('\n');
415
+ }
416
+ L.push('| Unit | Kind | Score | Gate | Phase | Needs |');
417
+ L.push('|---|---|---|---|---|---|');
418
+ for (const r of rows) {
419
+ L.push(`| ${r.unit} | ${r.kind} | ${r.score ?? '—'} | ${r.gateStatus ?? '—'} | ${r.youAreHere} | ${unitStatusLabel(r)} |`);
420
+ }
421
+ L.push('');
422
+ const blocked = rows.filter((r) => r.started && (r.obligationsOpen > 0));
423
+ const toReview = rows.filter((r) => r.started && r.obligationsOpen === 0 && !r.signedOff && r.reviewQueue >= 0);
424
+ const notStarted = rows.filter((r) => !r.started);
425
+ L.push(`**${rows.filter((r) => r.signedOff).length}/${rows.length} signed off** · ${blocked.length} with open obligations · ${notStarted.length} not started.`);
426
+ L.push('');
427
+ L.push('## → Next (review is serial — one unit at a time)');
428
+ const next = blocked[0] || toReview[0] || notStarted[0];
429
+ if (!next) L.push('All units signed off — `sungen delivery` to export.');
430
+ else if (!next.started) L.push(`Generate **${next.unit}** — \`/sungen:create-test ${next.unit}\`.`);
431
+ else L.push(`Review **${next.unit}** (${unitStatusLabel(next)}) — \`sungen journey --screen ${next.unit}\`, then sign off.`);
432
+ L.push('');
433
+ return L.join('\n');
434
+ }
@@ -42,6 +42,26 @@ export function isolationRisk(featureText: string, stateful: boolean): boolean {
42
42
  return !/@cleanup:cart\b|@isolate\b|empty cart|fresh (?:browser )?context|new context/i.test(featureText);
43
43
  }
44
44
 
45
+ /**
46
+ * #414 — the complement of isolationRisk. Per-scenario isolation is the DEFAULT now, so untagged
47
+ * suites can't cascade. `@serial` opts INTO a shared-page `test.describe.serial` suite, whose
48
+ * Playwright semantics SKIP every test after the first failure. That is correct ONLY for a genuinely
49
+ * stateful ordered sequence; for INDEPENDENT scenarios — each re-establishing its own start state via
50
+ * a navigating Background — `@serial` buys no correctness, only a cascade that *masks* failures (one
51
+ * early red turns the rest into greens-by-omission). Returns the scenario count when `@serial` should
52
+ * be dropped (the default isolation is safer), else 0.
53
+ *
54
+ * Conservative on purpose — only fires on `@serial` suites whose every scenario provably restarts (a
55
+ * `Background` that navigates `on [..] page`), so a real stateful sequence keeps its `@serial`.
56
+ */
57
+ export function serialCascadeRisk(featureText: string, scenarioCount: number): number {
58
+ if (!/@serial\b/i.test(featureText)) return 0; // isolated by default → no cascade
59
+ if (scenarioCount < 8) return 0; // small blast radius — not worth the churn
60
+ const navBackground = /Background:[\s\S]*?\bon \[[^\]]+\] page\b/i.test(featureText);
61
+ if (!navBackground) return 0; // no per-scenario reset → genuinely stateful; @serial is right
62
+ return scenarioCount;
63
+ }
64
+
45
65
  export function downstreamScope(specText: string, scenarios: ScenarioInfo[]): DownstreamResult {
46
66
  const routes = downstreamRoutes(specText);
47
67
  const underCovered: { route: string; slug: string }[] = [];
@@ -59,9 +59,17 @@ export function loadCatalog(): Catalog {
59
59
  return parseYaml(fs.readFileSync(p, 'utf-8')) as Catalog;
60
60
  }
61
61
 
62
+ // Word-aware keyword match: `\b<kw>(s|es|ed|ing)?\b`, so a keyword matches whole words (plus
63
+ // simple plural/verb suffixes) but NOT as a substring of a larger word — e.g. `form` must not
64
+ // match inside `Format` or `@platform`, while `product` still matches `products`. Multi-word
65
+ // keywords ("add to cart", "is hidden") match the phrase with a boundary at each end.
66
+ const wordRe = (kw: string) => {
67
+ const esc = kw.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
+ return new RegExp(`\\b${esc}(?:s|es|ed|ing)?\\b`);
69
+ };
62
70
  const has = (haystacks: string[], kw: string) => {
63
- const k = kw.toLowerCase();
64
- return haystacks.some((h) => h.includes(k));
71
+ const re = wordRe(kw);
72
+ return haystacks.some((h) => re.test(h));
65
73
  };
66
74
 
67
75
  // ---------- Sensor 1: Viewpoint Gate (catalog-driven) ----------
@@ -75,16 +83,21 @@ export interface GateResult {
75
83
  universalGaps: string[];
76
84
  }
77
85
 
78
- export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEntry[], catalog: Catalog): GateResult {
86
+ export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEntry[], catalog: Catalog, isMobile = false): GateResult {
79
87
  const haystacks = [
80
88
  ...scenarios.map((s) => s.haystack),
81
89
  ...viewpoints.map((v) => `${v.id} ${v.reason}`.toLowerCase()),
82
90
  ];
83
91
 
84
- // Detect page-type by detect_keywords hit count
92
+ // Detect page-type by detect_keywords hit count. `mobile-*` page-types are candidates ONLY for
93
+ // mobile targets; web targets never consider them. Mobile targets still consider the web types
94
+ // too, so a mobile commerce/form screen is selected as ecommerce-list/form by keyword fit —
95
+ // `mobile-home` does not crowd out a better-fitting type. With no type matching → pageType stays
96
+ // null → no themes required → the gate passes leniently rather than false-FAILing.
85
97
  let pageType: string | null = null;
86
98
  let best = 0;
87
99
  for (const [pt, def] of Object.entries(catalog.page_types)) {
100
+ if (pt.startsWith('mobile-') && !isMobile) continue;
88
101
  const hits = def.detect_keywords.filter((k) => has(haystacks, k)).length;
89
102
  if (hits > best) { best = hits; pageType = pt; }
90
103
  }
@@ -97,10 +110,14 @@ export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEn
97
110
  // Scenarios that cover this theme by keyword (include @manual — a manual
98
111
  // scenario with a real assertion still covers the viewpoint for design).
99
112
  const coverers = scenarios.filter((s) => t.keywords.some((k) => s.haystack.includes(k.toLowerCase())));
100
- const deep = coverers.some((s) => s.hasDataAssertion);
101
- if (deep) covered++;
102
- else if (coverers.length > 0) gaps.push({ theme: t.theme, keywords: t.keywords, status: 'shallow' });
103
- else gaps.push({ theme: t.theme, keywords: t.keywords, status: 'missing' });
113
+ if (coverers.length === 0) { gaps.push({ theme: t.theme, keywords: t.keywords, status: 'missing' }); continue; }
114
+ // A theme WITHOUT `depth:` is visibility/navigation — landing/seeing/leaving IS the
115
+ // assertion, so mere presence covers it. Only DATA-correctness themes (depth.requires:
116
+ // data-assertion) need a data assertion to count as deeply covered.
117
+ const needsDepth = t.depth?.requires === 'data-assertion';
118
+ if (!needsDepth) { covered++; continue; }
119
+ if (coverers.some((s) => s.hasDataAssertion)) covered++;
120
+ else gaps.push({ theme: t.theme, keywords: t.keywords, status: 'shallow' });
104
121
  }
105
122
  }
106
123