@sun-asterisk/sungen 3.2.34 → 3.3.0-alpha.2

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 (889) hide show
  1. package/dist/basis/anchor-grammar.d.ts +32 -0
  2. package/dist/basis/anchor-grammar.d.ts.map +1 -0
  3. package/dist/basis/anchor-grammar.js +82 -0
  4. package/dist/basis/anchor-grammar.js.map +1 -0
  5. package/dist/basis/anchor-hash.d.ts +6 -0
  6. package/dist/basis/anchor-hash.d.ts.map +1 -0
  7. package/dist/basis/anchor-hash.js +28 -0
  8. package/dist/basis/anchor-hash.js.map +1 -0
  9. package/dist/basis/bm51-tables.d.ts +20 -0
  10. package/dist/basis/bm51-tables.d.ts.map +1 -0
  11. package/dist/basis/bm51-tables.js +71 -0
  12. package/dist/basis/bm51-tables.js.map +1 -0
  13. package/dist/basis/extract-spec-anchors.d.ts +39 -0
  14. package/dist/basis/extract-spec-anchors.d.ts.map +1 -0
  15. package/dist/basis/extract-spec-anchors.js +81 -0
  16. package/dist/basis/extract-spec-anchors.js.map +1 -0
  17. package/dist/basis/migration-sentinel.d.ts +47 -0
  18. package/dist/basis/migration-sentinel.d.ts.map +1 -0
  19. package/dist/basis/migration-sentinel.js +219 -0
  20. package/dist/basis/migration-sentinel.js.map +1 -0
  21. package/dist/basis/spec-frontmatter.d.ts +18 -0
  22. package/dist/basis/spec-frontmatter.d.ts.map +1 -0
  23. package/dist/basis/spec-frontmatter.js +55 -0
  24. package/dist/basis/spec-frontmatter.js.map +1 -0
  25. package/dist/basis/spec-sections.d.ts +18 -0
  26. package/dist/basis/spec-sections.d.ts.map +1 -0
  27. package/dist/basis/spec-sections.js +27 -0
  28. package/dist/basis/spec-sections.js.map +1 -0
  29. package/dist/basis/test-basis-schema.d.ts +28 -0
  30. package/dist/basis/test-basis-schema.d.ts.map +1 -0
  31. package/dist/basis/test-basis-schema.js +141 -0
  32. package/dist/basis/test-basis-schema.js.map +1 -0
  33. package/dist/capabilities/discover.d.ts +4 -0
  34. package/dist/capabilities/discover.d.ts.map +1 -1
  35. package/dist/capabilities/discover.js +16 -2
  36. package/dist/capabilities/discover.js.map +1 -1
  37. package/dist/capabilities/registry.d.ts +15 -0
  38. package/dist/capabilities/registry.d.ts.map +1 -1
  39. package/dist/capabilities/registry.js.map +1 -1
  40. package/dist/cli/commands/audit.d.ts.map +1 -1
  41. package/dist/cli/commands/audit.js +10 -5
  42. package/dist/cli/commands/audit.js.map +1 -1
  43. package/dist/cli/commands/capability.d.ts.map +1 -1
  44. package/dist/cli/commands/capability.js +6 -2
  45. package/dist/cli/commands/capability.js.map +1 -1
  46. package/dist/cli/commands/contract-derive-command.d.ts +11 -0
  47. package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
  48. package/dist/cli/commands/contract-derive-command.js +99 -0
  49. package/dist/cli/commands/contract-derive-command.js.map +1 -0
  50. package/dist/cli/commands/contract.d.ts +17 -0
  51. package/dist/cli/commands/contract.d.ts.map +1 -0
  52. package/dist/cli/commands/contract.js +162 -0
  53. package/dist/cli/commands/contract.js.map +1 -0
  54. package/dist/cli/commands/defect.d.ts +7 -0
  55. package/dist/cli/commands/defect.d.ts.map +1 -0
  56. package/dist/cli/commands/defect.js +68 -0
  57. package/dist/cli/commands/defect.js.map +1 -0
  58. package/dist/cli/commands/delivery.d.ts.map +1 -1
  59. package/dist/cli/commands/delivery.js +96 -12
  60. package/dist/cli/commands/delivery.js.map +1 -1
  61. package/dist/cli/commands/generate.d.ts.map +1 -1
  62. package/dist/cli/commands/generate.js +3 -1
  63. package/dist/cli/commands/generate.js.map +1 -1
  64. package/dist/cli/commands/journey.d.ts.map +1 -1
  65. package/dist/cli/commands/journey.js +43 -15
  66. package/dist/cli/commands/journey.js.map +1 -1
  67. package/dist/cli/commands/next.d.ts +8 -0
  68. package/dist/cli/commands/next.d.ts.map +1 -1
  69. package/dist/cli/commands/next.js +68 -8
  70. package/dist/cli/commands/next.js.map +1 -1
  71. package/dist/cli/commands/triage.d.ts +7 -0
  72. package/dist/cli/commands/triage.d.ts.map +1 -0
  73. package/dist/cli/commands/triage.js +98 -0
  74. package/dist/cli/commands/triage.js.map +1 -0
  75. package/dist/cli/commands/update.d.ts +11 -0
  76. package/dist/cli/commands/update.d.ts.map +1 -1
  77. package/dist/cli/commands/update.js +31 -6
  78. package/dist/cli/commands/update.js.map +1 -1
  79. package/dist/cli/index.js +24 -33
  80. package/dist/cli/index.js.map +1 -1
  81. package/dist/cli/project-sungen-invocation.d.ts +7 -0
  82. package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
  83. package/dist/cli/project-sungen-invocation.js +76 -0
  84. package/dist/cli/project-sungen-invocation.js.map +1 -0
  85. package/dist/contract/approve-contract.d.ts +31 -0
  86. package/dist/contract/approve-contract.d.ts.map +1 -0
  87. package/dist/contract/approve-contract.js +87 -0
  88. package/dist/contract/approve-contract.js.map +1 -0
  89. package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
  90. package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
  91. package/dist/contract/assertion-matcher-vocabulary.js +100 -0
  92. package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
  93. package/dist/contract/build-contract-verdict.d.ts +32 -0
  94. package/dist/contract/build-contract-verdict.d.ts.map +1 -0
  95. package/dist/contract/build-contract-verdict.js +123 -0
  96. package/dist/contract/build-contract-verdict.js.map +1 -0
  97. package/dist/contract/build-trace-conformance.d.ts +18 -0
  98. package/dist/contract/build-trace-conformance.d.ts.map +1 -0
  99. package/dist/contract/build-trace-conformance.js +153 -0
  100. package/dist/contract/build-trace-conformance.js.map +1 -0
  101. package/dist/contract/classify-contract-change.d.ts +23 -0
  102. package/dist/contract/classify-contract-change.d.ts.map +1 -0
  103. package/dist/contract/classify-contract-change.js +98 -0
  104. package/dist/contract/classify-contract-change.js.map +1 -0
  105. package/dist/contract/classify-contract-unit.d.ts +18 -0
  106. package/dist/contract/classify-contract-unit.d.ts.map +1 -0
  107. package/dist/contract/classify-contract-unit.js +49 -0
  108. package/dist/contract/classify-contract-unit.js.map +1 -0
  109. package/dist/contract/classify-trace-conformance.d.ts +32 -0
  110. package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
  111. package/dist/contract/classify-trace-conformance.js +117 -0
  112. package/dist/contract/classify-trace-conformance.js.map +1 -0
  113. package/dist/contract/contract-git.d.ts +24 -0
  114. package/dist/contract/contract-git.d.ts.map +1 -0
  115. package/dist/contract/contract-git.js +144 -0
  116. package/dist/contract/contract-git.js.map +1 -0
  117. package/dist/contract/contract-hash.d.ts +7 -0
  118. package/dist/contract/contract-hash.d.ts.map +1 -0
  119. package/dist/contract/contract-hash.js +38 -0
  120. package/dist/contract/contract-hash.js.map +1 -0
  121. package/dist/contract/contract-report-file.d.ts +4 -0
  122. package/dist/contract/contract-report-file.d.ts.map +1 -0
  123. package/dist/contract/contract-report-file.js +60 -0
  124. package/dist/contract/contract-report-file.js.map +1 -0
  125. package/dist/contract/contract-schema.d.ts +71 -0
  126. package/dist/contract/contract-schema.d.ts.map +1 -0
  127. package/dist/contract/contract-schema.js +102 -0
  128. package/dist/contract/contract-schema.js.map +1 -0
  129. package/dist/contract/contract-units.d.ts +12 -0
  130. package/dist/contract/contract-units.d.ts.map +1 -0
  131. package/dist/contract/contract-units.js +96 -0
  132. package/dist/contract/contract-units.js.map +1 -0
  133. package/dist/contract/contracts-lock-file.d.ts +24 -0
  134. package/dist/contract/contracts-lock-file.d.ts.map +1 -0
  135. package/dist/contract/contracts-lock-file.js +140 -0
  136. package/dist/contract/contracts-lock-file.js.map +1 -0
  137. package/dist/contract/data-normalize.d.ts +10 -0
  138. package/dist/contract/data-normalize.d.ts.map +1 -0
  139. package/dist/contract/data-normalize.js +110 -0
  140. package/dist/contract/data-normalize.js.map +1 -0
  141. package/dist/contract/derive-free-feature.d.ts +38 -0
  142. package/dist/contract/derive-free-feature.d.ts.map +1 -0
  143. package/dist/contract/derive-free-feature.js +154 -0
  144. package/dist/contract/derive-free-feature.js.map +1 -0
  145. package/dist/contract/field-semantic-redaction.d.ts +21 -0
  146. package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
  147. package/dist/contract/field-semantic-redaction.js +118 -0
  148. package/dist/contract/field-semantic-redaction.js.map +1 -0
  149. package/dist/contract/free-feature-blocks.d.ts +10 -0
  150. package/dist/contract/free-feature-blocks.d.ts.map +1 -0
  151. package/dist/contract/free-feature-blocks.js +94 -0
  152. package/dist/contract/free-feature-blocks.js.map +1 -0
  153. package/dist/contract/lock-secret-guard.d.ts +22 -0
  154. package/dist/contract/lock-secret-guard.d.ts.map +1 -0
  155. package/dist/contract/lock-secret-guard.js +134 -0
  156. package/dist/contract/lock-secret-guard.js.map +1 -0
  157. package/dist/contract/lock-vs-git-head.d.ts +26 -0
  158. package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
  159. package/dist/contract/lock-vs-git-head.js +28 -0
  160. package/dist/contract/lock-vs-git-head.js.map +1 -0
  161. package/dist/contract/normalize-contract-step.d.ts +21 -0
  162. package/dist/contract/normalize-contract-step.d.ts.map +1 -0
  163. package/dist/contract/normalize-contract-step.js +70 -0
  164. package/dist/contract/normalize-contract-step.js.map +1 -0
  165. package/dist/contract/normalize-feature-steps.d.ts +7 -0
  166. package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
  167. package/dist/contract/normalize-feature-steps.js +127 -0
  168. package/dist/contract/normalize-feature-steps.js.map +1 -0
  169. package/dist/contract/oracle-strength.d.ts +12 -0
  170. package/dist/contract/oracle-strength.d.ts.map +1 -0
  171. package/dist/contract/oracle-strength.js +88 -0
  172. package/dist/contract/oracle-strength.js.map +1 -0
  173. package/dist/contract/project-contract-blockers.d.ts +10 -0
  174. package/dist/contract/project-contract-blockers.d.ts.map +1 -0
  175. package/dist/contract/project-contract-blockers.js +73 -0
  176. package/dist/contract/project-contract-blockers.js.map +1 -0
  177. package/dist/contract/record-contract-approval.d.ts +21 -0
  178. package/dist/contract/record-contract-approval.d.ts.map +1 -0
  179. package/dist/contract/record-contract-approval.js +98 -0
  180. package/dist/contract/record-contract-approval.js.map +1 -0
  181. package/dist/contract/render-free-gherkin.d.ts +40 -0
  182. package/dist/contract/render-free-gherkin.d.ts.map +1 -0
  183. package/dist/contract/render-free-gherkin.js +141 -0
  184. package/dist/contract/render-free-gherkin.js.map +1 -0
  185. package/dist/contract/render-trace-sentence.d.ts +20 -0
  186. package/dist/contract/render-trace-sentence.d.ts.map +1 -0
  187. package/dist/contract/render-trace-sentence.js +109 -0
  188. package/dist/contract/render-trace-sentence.js.map +1 -0
  189. package/dist/contract/render-trace-target.d.ts +14 -0
  190. package/dist/contract/render-trace-target.d.ts.map +1 -0
  191. package/dist/contract/render-trace-target.js +70 -0
  192. package/dist/contract/render-trace-target.js.map +1 -0
  193. package/dist/contract/resolve-unit-trace-identities.d.ts +8 -0
  194. package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
  195. package/dist/contract/resolve-unit-trace-identities.js +78 -0
  196. package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
  197. package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
  198. package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
  199. package/dist/contract/trace/collect-trace-evidence.js +161 -0
  200. package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
  201. package/dist/contract/trace/identity-cache.d.ts +19 -0
  202. package/dist/contract/trace/identity-cache.d.ts.map +1 -0
  203. package/dist/contract/trace/identity-cache.js +104 -0
  204. package/dist/contract/trace/identity-cache.js.map +1 -0
  205. package/dist/contract/trace/select-final-attempt.d.ts +27 -0
  206. package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
  207. package/dist/contract/trace/select-final-attempt.js +116 -0
  208. package/dist/contract/trace/select-final-attempt.js.map +1 -0
  209. package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
  210. package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
  211. package/dist/contract/trace/snapshot-html-renderer.js +172 -0
  212. package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
  213. package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
  214. package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
  215. package/dist/contract/trace/snapshot-resolver.js +65 -0
  216. package/dist/contract/trace/snapshot-resolver.js.map +1 -0
  217. package/dist/contract/trace/snapshot-store.d.ts +66 -0
  218. package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
  219. package/dist/contract/trace/snapshot-store.js +173 -0
  220. package/dist/contract/trace/snapshot-store.js.map +1 -0
  221. package/dist/contract/trace/target-identity.d.ts +74 -0
  222. package/dist/contract/trace/target-identity.d.ts.map +1 -0
  223. package/dist/contract/trace/target-identity.js +68 -0
  224. package/dist/contract/trace/target-identity.js.map +1 -0
  225. package/dist/contract/trace/test-step-join.d.ts +65 -0
  226. package/dist/contract/trace/test-step-join.d.ts.map +1 -0
  227. package/dist/contract/trace/test-step-join.js +80 -0
  228. package/dist/contract/trace/test-step-join.js.map +1 -0
  229. package/dist/contract/trace/trace-binding.d.ts +21 -0
  230. package/dist/contract/trace/trace-binding.d.ts.map +1 -0
  231. package/dist/contract/trace/trace-binding.js +88 -0
  232. package/dist/contract/trace/trace-binding.js.map +1 -0
  233. package/dist/contract/trace/trace-file-reader.d.ts +41 -0
  234. package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
  235. package/dist/contract/trace/trace-file-reader.js +161 -0
  236. package/dist/contract/trace/trace-file-reader.js.map +1 -0
  237. package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
  238. package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
  239. package/dist/contract/trace/trace-fingerprint.js +96 -0
  240. package/dist/contract/trace/trace-fingerprint.js.map +1 -0
  241. package/dist/contract/trace/trace-identity-resolution.d.ts +26 -0
  242. package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
  243. package/dist/contract/trace/trace-identity-resolution.js +84 -0
  244. package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
  245. package/dist/contract/trace/trace-run-record.d.ts +30 -0
  246. package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
  247. package/dist/contract/trace/trace-run-record.js +121 -0
  248. package/dist/contract/trace/trace-run-record.js.map +1 -0
  249. package/dist/contract/trace/trace-steps.d.ts +19 -0
  250. package/dist/contract/trace/trace-steps.d.ts.map +1 -0
  251. package/dist/contract/trace/trace-steps.js +79 -0
  252. package/dist/contract/trace/trace-steps.js.map +1 -0
  253. package/dist/contract/trace/trace-zip-limits.d.ts +45 -0
  254. package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
  255. package/dist/contract/trace/trace-zip-limits.js +52 -0
  256. package/dist/contract/trace/trace-zip-limits.js.map +1 -0
  257. package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
  258. package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
  259. package/dist/contract/trace/trace-zip-reader.js +186 -0
  260. package/dist/contract/trace/trace-zip-reader.js.map +1 -0
  261. package/dist/contract/verify-approval-decisions.d.ts +5 -0
  262. package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
  263. package/dist/contract/verify-approval-decisions.js +104 -0
  264. package/dist/contract/verify-approval-decisions.js.map +1 -0
  265. package/dist/decisions/append-decision.d.ts +12 -0
  266. package/dist/decisions/append-decision.d.ts.map +1 -0
  267. package/dist/decisions/append-decision.js +73 -0
  268. package/dist/decisions/append-decision.js.map +1 -0
  269. package/dist/decisions/confirmed-decision-commands.d.ts +41 -0
  270. package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
  271. package/dist/decisions/confirmed-decision-commands.js +190 -0
  272. package/dist/decisions/confirmed-decision-commands.js.map +1 -0
  273. package/dist/decisions/decision-file.d.ts +31 -0
  274. package/dist/decisions/decision-file.d.ts.map +1 -0
  275. package/dist/decisions/decision-file.js +139 -0
  276. package/dist/decisions/decision-file.js.map +1 -0
  277. package/dist/decisions/decision-identity.d.ts +12 -0
  278. package/dist/decisions/decision-identity.d.ts.map +1 -0
  279. package/dist/decisions/decision-identity.js +78 -0
  280. package/dist/decisions/decision-identity.js.map +1 -0
  281. package/dist/decisions/decision-integrity-check.d.ts +12 -0
  282. package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
  283. package/dist/decisions/decision-integrity-check.js +102 -0
  284. package/dist/decisions/decision-integrity-check.js.map +1 -0
  285. package/dist/decisions/decision-schema.d.ts +42 -0
  286. package/dist/decisions/decision-schema.d.ts.map +1 -0
  287. package/dist/decisions/decision-schema.js +75 -0
  288. package/dist/decisions/decision-schema.js.map +1 -0
  289. package/dist/decisions/decision-source.d.ts +2 -0
  290. package/dist/decisions/decision-source.d.ts.map +1 -0
  291. package/dist/decisions/decision-source.js +18 -0
  292. package/dist/decisions/decision-source.js.map +1 -0
  293. package/dist/decisions/exclusive-file-create.d.ts +3 -0
  294. package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
  295. package/dist/decisions/exclusive-file-create.js +84 -0
  296. package/dist/decisions/exclusive-file-create.js.map +1 -0
  297. package/dist/decisions/git-head-trust.d.ts +7 -0
  298. package/dist/decisions/git-head-trust.d.ts.map +1 -0
  299. package/dist/decisions/git-head-trust.js +76 -0
  300. package/dist/decisions/git-head-trust.js.map +1 -0
  301. package/dist/decisions/human-confirmation.d.ts +35 -0
  302. package/dist/decisions/human-confirmation.d.ts.map +1 -0
  303. package/dist/decisions/human-confirmation.js +161 -0
  304. package/dist/decisions/human-confirmation.js.map +1 -0
  305. package/dist/decisions/legacy-waiver-migration.d.ts +31 -0
  306. package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
  307. package/dist/decisions/legacy-waiver-migration.js +170 -0
  308. package/dist/decisions/legacy-waiver-migration.js.map +1 -0
  309. package/dist/decisions/read-decisions.d.ts +19 -0
  310. package/dist/decisions/read-decisions.d.ts.map +1 -0
  311. package/dist/decisions/read-decisions.js +90 -0
  312. package/dist/decisions/read-decisions.js.map +1 -0
  313. package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
  314. package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
  315. package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
  316. package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
  317. package/dist/decisions/waiver-decisions.d.ts +24 -0
  318. package/dist/decisions/waiver-decisions.d.ts.map +1 -0
  319. package/dist/decisions/waiver-decisions.js +46 -0
  320. package/dist/decisions/waiver-decisions.js.map +1 -0
  321. package/dist/exporters/csv-exporter.d.ts +4 -0
  322. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  323. package/dist/exporters/csv-exporter.js +1 -1
  324. package/dist/exporters/csv-exporter.js.map +1 -1
  325. package/dist/exporters/feature-parser.d.ts +8 -0
  326. package/dist/exporters/feature-parser.d.ts.map +1 -1
  327. package/dist/exporters/feature-parser.js +12 -0
  328. package/dist/exporters/feature-parser.js.map +1 -1
  329. package/dist/exporters/matrix/build.d.ts +3 -0
  330. package/dist/exporters/matrix/build.d.ts.map +1 -1
  331. package/dist/exporters/matrix/build.js +59 -19
  332. package/dist/exporters/matrix/build.js.map +1 -1
  333. package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
  334. package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
  335. package/dist/exporters/matrix/carry-manual-results.js +76 -6
  336. package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
  337. package/dist/exporters/matrix/export.d.ts +8 -2
  338. package/dist/exporters/matrix/export.d.ts.map +1 -1
  339. package/dist/exporters/matrix/export.js +16 -3
  340. package/dist/exporters/matrix/export.js.map +1 -1
  341. package/dist/exporters/matrix/gates.d.ts +6 -1
  342. package/dist/exporters/matrix/gates.d.ts.map +1 -1
  343. package/dist/exporters/matrix/gates.js +57 -128
  344. package/dist/exporters/matrix/gates.js.map +1 -1
  345. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  346. package/dist/exporters/matrix/render-csv.js +4 -1
  347. package/dist/exporters/matrix/render-csv.js.map +1 -1
  348. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
  349. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
  350. package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
  351. package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
  352. package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
  353. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  354. package/dist/exporters/matrix/render-xlsx.js +6 -0
  355. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  356. package/dist/exporters/matrix/types.d.ts +9 -1
  357. package/dist/exporters/matrix/types.d.ts.map +1 -1
  358. package/dist/exporters/matrix/types.js.map +1 -1
  359. package/dist/exporters/matrix/xlsx-cells.js +1 -1
  360. package/dist/exporters/matrix/xlsx-cells.js.map +1 -1
  361. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  362. package/dist/exporters/playwright-report-parser.js +1 -0
  363. package/dist/exporters/playwright-report-parser.js.map +1 -1
  364. package/dist/exporters/scenario-merger.d.ts.map +1 -1
  365. package/dist/exporters/scenario-merger.js +2 -0
  366. package/dist/exporters/scenario-merger.js.map +1 -1
  367. package/dist/exporters/types.d.ts +5 -0
  368. package/dist/exporters/types.d.ts.map +1 -1
  369. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  370. package/dist/generators/test-generator/code-generator.js +13 -0
  371. package/dist/generators/test-generator/code-generator.js.map +1 -1
  372. package/dist/graph/evaluate-graph.d.ts +23 -0
  373. package/dist/graph/evaluate-graph.d.ts.map +1 -0
  374. package/dist/graph/evaluate-graph.js +52 -0
  375. package/dist/graph/evaluate-graph.js.map +1 -0
  376. package/dist/graph/locate-finding.d.ts +20 -0
  377. package/dist/graph/locate-finding.d.ts.map +1 -0
  378. package/dist/graph/locate-finding.js +137 -0
  379. package/dist/graph/locate-finding.js.map +1 -0
  380. package/dist/graph/node-decl.d.ts +66 -0
  381. package/dist/graph/node-decl.d.ts.map +1 -0
  382. package/dist/graph/node-decl.js +8 -0
  383. package/dist/graph/node-decl.js.map +1 -0
  384. package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
  385. package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
  386. package/dist/graph/nodes/core-node-catalog.js +13 -0
  387. package/dist/graph/nodes/core-node-catalog.js.map +1 -0
  388. package/dist/graph/nodes/test-case-contract-nodes.d.ts +21 -0
  389. package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
  390. package/dist/graph/nodes/test-case-contract-nodes.js +59 -0
  391. package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
  392. package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
  393. package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
  394. package/dist/graph/nodes/test-case-nodes.js +66 -0
  395. package/dist/graph/nodes/test-case-nodes.js.map +1 -0
  396. package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
  397. package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
  398. package/dist/graph/nodes/unit-audit-nodes.js +95 -0
  399. package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
  400. package/dist/graph/nodes/unit-contract-node.d.ts +21 -0
  401. package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
  402. package/dist/graph/nodes/unit-contract-node.js +88 -0
  403. package/dist/graph/nodes/unit-contract-node.js.map +1 -0
  404. package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
  405. package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
  406. package/dist/graph/nodes/unit-design-nodes.js +91 -0
  407. package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
  408. package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
  409. package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
  410. package/dist/graph/nodes/unit-execution-nodes.js +79 -0
  411. package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
  412. package/dist/graph/test-case-identity.d.ts +13 -0
  413. package/dist/graph/test-case-identity.d.ts.map +1 -0
  414. package/dist/graph/test-case-identity.js +106 -0
  415. package/dist/graph/test-case-identity.js.map +1 -0
  416. package/dist/graph/test-case-results.d.ts +11 -0
  417. package/dist/graph/test-case-results.d.ts.map +1 -0
  418. package/dist/graph/test-case-results.js +137 -0
  419. package/dist/graph/test-case-results.js.map +1 -0
  420. package/dist/graph/test-case-satisfied.d.ts +25 -0
  421. package/dist/graph/test-case-satisfied.d.ts.map +1 -0
  422. package/dist/graph/test-case-satisfied.js +51 -0
  423. package/dist/graph/test-case-satisfied.js.map +1 -0
  424. package/dist/graph/test-case-state-shape.d.ts +67 -0
  425. package/dist/graph/test-case-state-shape.d.ts.map +1 -0
  426. package/dist/graph/test-case-state-shape.js +3 -0
  427. package/dist/graph/test-case-state-shape.js.map +1 -0
  428. package/dist/graph/test-case-states.d.ts +9 -0
  429. package/dist/graph/test-case-states.d.ts.map +1 -0
  430. package/dist/graph/test-case-states.js +116 -0
  431. package/dist/graph/test-case-states.js.map +1 -0
  432. package/dist/graph/unit-artifact-probes.d.ts +19 -0
  433. package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
  434. package/dist/graph/unit-artifact-probes.js +133 -0
  435. package/dist/graph/unit-artifact-probes.js.map +1 -0
  436. package/dist/graph/unit-state-facts.d.ts +43 -0
  437. package/dist/graph/unit-state-facts.d.ts.map +1 -0
  438. package/dist/graph/unit-state-facts.js +87 -0
  439. package/dist/graph/unit-state-facts.js.map +1 -0
  440. package/dist/harness/audit.d.ts +11 -0
  441. package/dist/harness/audit.d.ts.map +1 -1
  442. package/dist/harness/audit.js +34 -9
  443. package/dist/harness/audit.js.map +1 -1
  444. package/dist/harness/catalog/gate-rules.yaml +647 -0
  445. package/dist/harness/counted-assertions.d.ts +2 -0
  446. package/dist/harness/counted-assertions.d.ts.map +1 -0
  447. package/dist/harness/counted-assertions.js +38 -0
  448. package/dist/harness/counted-assertions.js.map +1 -0
  449. package/dist/harness/defect-draft.d.ts +14 -0
  450. package/dist/harness/defect-draft.d.ts.map +1 -0
  451. package/dist/harness/defect-draft.js +143 -0
  452. package/dist/harness/defect-draft.js.map +1 -0
  453. package/dist/harness/defects.d.ts +24 -0
  454. package/dist/harness/defects.d.ts.map +1 -0
  455. package/dist/harness/defects.js +118 -0
  456. package/dist/harness/defects.js.map +1 -0
  457. package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
  458. package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
  459. package/dist/harness/gates/audit-gate-terms.js +72 -0
  460. package/dist/harness/gates/audit-gate-terms.js.map +1 -0
  461. package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
  462. package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
  463. package/dist/harness/gates/audit-gate-verdict.js +101 -0
  464. package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
  465. package/dist/harness/gates/audit-input-hash.d.ts +4 -0
  466. package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
  467. package/dist/harness/gates/audit-input-hash.js +96 -0
  468. package/dist/harness/gates/audit-input-hash.js.map +1 -0
  469. package/dist/harness/gates/audit-report-verification.d.ts +41 -0
  470. package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
  471. package/dist/harness/gates/audit-report-verification.js +236 -0
  472. package/dist/harness/gates/audit-report-verification.js.map +1 -0
  473. package/dist/harness/gates/decisions-hash.d.ts +13 -0
  474. package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
  475. package/dist/harness/gates/decisions-hash.js +72 -0
  476. package/dist/harness/gates/decisions-hash.js.map +1 -0
  477. package/dist/harness/gates/delivery-checklist.d.ts +21 -0
  478. package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
  479. package/dist/harness/gates/delivery-checklist.js +128 -0
  480. package/dist/harness/gates/delivery-checklist.js.map +1 -0
  481. package/dist/harness/gates/delivery-decision-gate.d.ts +33 -0
  482. package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
  483. package/dist/harness/gates/delivery-decision-gate.js +116 -0
  484. package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
  485. package/dist/harness/gates/delivery-decision-step.d.ts +19 -0
  486. package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
  487. package/dist/harness/gates/delivery-decision-step.js +86 -0
  488. package/dist/harness/gates/delivery-decision-step.js.map +1 -0
  489. package/dist/harness/gates/gate-override-report.d.ts +59 -0
  490. package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
  491. package/dist/harness/gates/gate-override-report.js +86 -0
  492. package/dist/harness/gates/gate-override-report.js.map +1 -0
  493. package/dist/harness/gates/gate-override-scope.d.ts +18 -0
  494. package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
  495. package/dist/harness/gates/gate-override-scope.js +98 -0
  496. package/dist/harness/gates/gate-override-scope.js.map +1 -0
  497. package/dist/harness/gates/gate-override-validation.d.ts +25 -0
  498. package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
  499. package/dist/harness/gates/gate-override-validation.js +81 -0
  500. package/dist/harness/gates/gate-override-validation.js.map +1 -0
  501. package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
  502. package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
  503. package/dist/harness/gates/gate-rule-catalog.js +112 -0
  504. package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
  505. package/dist/harness/gates/gate-terms.d.ts +33 -0
  506. package/dist/harness/gates/gate-terms.d.ts.map +1 -0
  507. package/dist/harness/gates/gate-terms.js +41 -0
  508. package/dist/harness/gates/gate-terms.js.map +1 -0
  509. package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
  510. package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
  511. package/dist/harness/gates/in-run-decision-check.js +138 -0
  512. package/dist/harness/gates/in-run-decision-check.js.map +1 -0
  513. package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
  514. package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
  515. package/dist/harness/gates/journey-gate-inputs.js +81 -0
  516. package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
  517. package/dist/harness/gates/migrated-waiver-review.d.ts +6 -0
  518. package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
  519. package/dist/harness/gates/migrated-waiver-review.js +116 -0
  520. package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
  521. package/dist/harness/gates/resolve-rule.d.ts +59 -0
  522. package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
  523. package/dist/harness/gates/resolve-rule.js +41 -0
  524. package/dist/harness/gates/resolve-rule.js.map +1 -0
  525. package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
  526. package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
  527. package/dist/harness/gates/unit-gate-overrides.js +122 -0
  528. package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
  529. package/dist/harness/journey.d.ts +43 -1
  530. package/dist/harness/journey.d.ts.map +1 -1
  531. package/dist/harness/journey.js +207 -33
  532. package/dist/harness/journey.js.map +1 -1
  533. package/dist/harness/manifest.d.ts +12 -0
  534. package/dist/harness/manifest.d.ts.map +1 -1
  535. package/dist/harness/manifest.js +73 -45
  536. package/dist/harness/manifest.js.map +1 -1
  537. package/dist/harness/next-step.d.ts +37 -6
  538. package/dist/harness/next-step.d.ts.map +1 -1
  539. package/dist/harness/next-step.js +34 -174
  540. package/dist/harness/next-step.js.map +1 -1
  541. package/dist/harness/parse.d.ts +25 -0
  542. package/dist/harness/parse.d.ts.map +1 -1
  543. package/dist/harness/parse.js +38 -13
  544. package/dist/harness/parse.js.map +1 -1
  545. package/dist/harness/provenance.d.ts +29 -0
  546. package/dist/harness/provenance.d.ts.map +1 -1
  547. package/dist/harness/provenance.js +109 -9
  548. package/dist/harness/provenance.js.map +1 -1
  549. package/dist/harness/script-check.d.ts.map +1 -1
  550. package/dist/harness/script-check.js +29 -6
  551. package/dist/harness/script-check.js.map +1 -1
  552. package/dist/harness/sensors.d.ts +14 -3
  553. package/dist/harness/sensors.d.ts.map +1 -1
  554. package/dist/harness/sensors.js +64 -44
  555. package/dist/harness/sensors.js.map +1 -1
  556. package/dist/harness/triage-signals.d.ts +20 -0
  557. package/dist/harness/triage-signals.d.ts.map +1 -0
  558. package/dist/harness/triage-signals.js +30 -0
  559. package/dist/harness/triage-signals.js.map +1 -0
  560. package/dist/harness/triage-unit.d.ts +23 -0
  561. package/dist/harness/triage-unit.d.ts.map +1 -0
  562. package/dist/harness/triage-unit.js +142 -0
  563. package/dist/harness/triage-unit.js.map +1 -0
  564. package/dist/harness/triage.d.ts +28 -0
  565. package/dist/harness/triage.d.ts.map +1 -0
  566. package/dist/harness/triage.js +99 -0
  567. package/dist/harness/triage.js.map +1 -0
  568. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  569. package/dist/harness/viewpoint-ledger.js +2 -1
  570. package/dist/harness/viewpoint-ledger.js.map +1 -1
  571. package/dist/hooks/bash-write-targets.d.ts +15 -0
  572. package/dist/hooks/bash-write-targets.d.ts.map +1 -0
  573. package/dist/hooks/bash-write-targets.js +173 -0
  574. package/dist/hooks/bash-write-targets.js.map +1 -0
  575. package/dist/hooks/claude-hook-entry.d.ts +11 -0
  576. package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
  577. package/dist/hooks/claude-hook-entry.js +137 -0
  578. package/dist/hooks/claude-hook-entry.js.map +1 -0
  579. package/dist/hooks/claude-transcript-turn.d.ts +11 -0
  580. package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
  581. package/dist/hooks/claude-transcript-turn.js +143 -0
  582. package/dist/hooks/claude-transcript-turn.js.map +1 -0
  583. package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
  584. package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
  585. package/dist/hooks/guard-delivery-contract-rule.js +152 -0
  586. package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
  587. package/dist/hooks/guard-protected-paths.d.ts +9 -0
  588. package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
  589. package/dist/hooks/guard-protected-paths.js +107 -0
  590. package/dist/hooks/guard-protected-paths.js.map +1 -0
  591. package/dist/hooks/guard-rules.d.ts +37 -0
  592. package/dist/hooks/guard-rules.d.ts.map +1 -0
  593. package/dist/hooks/guard-rules.js +61 -0
  594. package/dist/hooks/guard-rules.js.map +1 -0
  595. package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
  596. package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
  597. package/dist/hooks/guard-tty-spoof-rule.js +21 -0
  598. package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
  599. package/dist/hooks/hand-back-core.d.ts +39 -0
  600. package/dist/hooks/hand-back-core.d.ts.map +1 -0
  601. package/dist/hooks/hand-back-core.js +122 -0
  602. package/dist/hooks/hand-back-core.js.map +1 -0
  603. package/dist/hooks/hook-cli-command.d.ts +9 -0
  604. package/dist/hooks/hook-cli-command.d.ts.map +1 -0
  605. package/dist/hooks/hook-cli-command.js +46 -0
  606. package/dist/hooks/hook-cli-command.js.map +1 -0
  607. package/dist/hooks/hook-registry.d.ts +10 -0
  608. package/dist/hooks/hook-registry.d.ts.map +1 -0
  609. package/dist/hooks/hook-registry.js +24 -0
  610. package/dist/hooks/hook-registry.js.map +1 -0
  611. package/dist/hooks/hook-result-contract.d.ts +50 -0
  612. package/dist/hooks/hook-result-contract.d.ts.map +1 -0
  613. package/dist/hooks/hook-result-contract.js +80 -0
  614. package/dist/hooks/hook-result-contract.js.map +1 -0
  615. package/dist/hooks/hook-toggles.d.ts +10 -0
  616. package/dist/hooks/hook-toggles.d.ts.map +1 -0
  617. package/dist/hooks/hook-toggles.js +107 -0
  618. package/dist/hooks/hook-toggles.js.map +1 -0
  619. package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
  620. package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
  621. package/dist/hooks/pre-tool-guard-hook.js +105 -0
  622. package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
  623. package/dist/hooks/session-start-unit-state-hook.d.ts +13 -0
  624. package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
  625. package/dist/hooks/session-start-unit-state-hook.js +57 -0
  626. package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
  627. package/dist/hooks/stop-next-step-hook.d.ts +16 -0
  628. package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
  629. package/dist/hooks/stop-next-step-hook.js +42 -0
  630. package/dist/hooks/stop-next-step-hook.js.map +1 -0
  631. package/dist/hooks/sungen-project-root.d.ts +19 -0
  632. package/dist/hooks/sungen-project-root.d.ts.map +1 -0
  633. package/dist/hooks/sungen-project-root.js +130 -0
  634. package/dist/hooks/sungen-project-root.js.map +1 -0
  635. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
  636. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
  637. package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
  638. package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
  639. package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
  640. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
  641. package/dist/orchestrator/ai-skills/commands.js +16 -6
  642. package/dist/orchestrator/ai-skills/commands.js.map +1 -1
  643. package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
  644. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -1
  645. package/dist/orchestrator/assets-drift.d.ts +5 -0
  646. package/dist/orchestrator/assets-drift.d.ts.map +1 -1
  647. package/dist/orchestrator/assets-drift.js +12 -3
  648. package/dist/orchestrator/assets-drift.js.map +1 -1
  649. package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
  650. package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
  651. package/dist/orchestrator/claude-hooks-install.js +203 -0
  652. package/dist/orchestrator/claude-hooks-install.js.map +1 -0
  653. package/dist/orchestrator/lockstep-version.d.ts +15 -5
  654. package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
  655. package/dist/orchestrator/lockstep-version.js +30 -17
  656. package/dist/orchestrator/lockstep-version.js.map +1 -1
  657. package/dist/orchestrator/project-initializer.d.ts +6 -0
  658. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  659. package/dist/orchestrator/project-initializer.js +24 -0
  660. package/dist/orchestrator/project-initializer.js.map +1 -1
  661. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  662. package/dist/orchestrator/screen-manager.js +4 -2
  663. package/dist/orchestrator/screen-manager.js.map +1 -1
  664. package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  665. package/dist/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  666. package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  667. package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  668. package/dist/orchestrator/templates/ai-src/commands/create-test.md +14 -16
  669. package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  670. package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  671. package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
  672. package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  673. package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
  674. package/dist/orchestrator/templates/ai-src/commands/review.md +2 -0
  675. package/dist/orchestrator/templates/ai-src/commands/run-test.md +14 -10
  676. package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
  677. package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
  678. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  679. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  680. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  681. package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  682. package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  683. package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  684. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
  685. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
  686. package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
  687. package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  688. package/dist/utils/safe-yaml-parse.d.ts +8 -0
  689. package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
  690. package/dist/utils/safe-yaml-parse.js +51 -0
  691. package/dist/utils/safe-yaml-parse.js.map +1 -0
  692. package/package.json +3 -3
  693. package/src/basis/anchor-grammar.ts +83 -0
  694. package/src/basis/anchor-hash.ts +27 -0
  695. package/src/basis/bm51-tables.ts +65 -0
  696. package/src/basis/extract-spec-anchors.ts +95 -0
  697. package/src/basis/migration-sentinel.ts +175 -0
  698. package/src/basis/spec-frontmatter.ts +68 -0
  699. package/src/basis/spec-sections.ts +35 -0
  700. package/src/basis/test-basis-schema.ts +81 -0
  701. package/src/capabilities/discover.ts +15 -2
  702. package/src/capabilities/registry.ts +16 -0
  703. package/src/cli/commands/audit.ts +10 -5
  704. package/src/cli/commands/capability.ts +4 -2
  705. package/src/cli/commands/contract-derive-command.ts +61 -0
  706. package/src/cli/commands/contract.ts +130 -0
  707. package/src/cli/commands/defect.ts +49 -0
  708. package/src/cli/commands/delivery.ts +92 -15
  709. package/src/cli/commands/generate.ts +3 -1
  710. package/src/cli/commands/journey.ts +41 -16
  711. package/src/cli/commands/next.ts +64 -10
  712. package/src/cli/commands/triage.ts +57 -0
  713. package/src/cli/commands/update.ts +35 -8
  714. package/src/cli/index.ts +24 -30
  715. package/src/cli/project-sungen-invocation.ts +39 -0
  716. package/src/contract/approve-contract.ts +94 -0
  717. package/src/contract/assertion-matcher-vocabulary.ts +93 -0
  718. package/src/contract/build-contract-verdict.ts +108 -0
  719. package/src/contract/build-trace-conformance.ts +100 -0
  720. package/src/contract/classify-contract-change.ts +92 -0
  721. package/src/contract/classify-contract-unit.ts +47 -0
  722. package/src/contract/classify-trace-conformance.ts +116 -0
  723. package/src/contract/contract-git.ts +106 -0
  724. package/src/contract/contract-hash.ts +34 -0
  725. package/src/contract/contract-report-file.ts +25 -0
  726. package/src/contract/contract-schema.ts +137 -0
  727. package/src/contract/contract-units.ts +52 -0
  728. package/src/contract/contracts-lock-file.ts +97 -0
  729. package/src/contract/data-normalize.ts +66 -0
  730. package/src/contract/derive-free-feature.ts +120 -0
  731. package/src/contract/field-semantic-redaction.ts +80 -0
  732. package/src/contract/free-feature-blocks.ts +56 -0
  733. package/src/contract/lock-secret-guard.ts +95 -0
  734. package/src/contract/lock-vs-git-head.ts +38 -0
  735. package/src/contract/normalize-contract-step.ts +74 -0
  736. package/src/contract/normalize-feature-steps.ts +93 -0
  737. package/src/contract/oracle-strength.ts +80 -0
  738. package/src/contract/project-contract-blockers.ts +36 -0
  739. package/src/contract/record-contract-approval.ts +70 -0
  740. package/src/contract/render-free-gherkin.ts +113 -0
  741. package/src/contract/render-trace-sentence.ts +98 -0
  742. package/src/contract/render-trace-target.ts +68 -0
  743. package/src/contract/resolve-unit-trace-identities.ts +35 -0
  744. package/src/contract/trace/collect-trace-evidence.ts +109 -0
  745. package/src/contract/trace/identity-cache.ts +61 -0
  746. package/src/contract/trace/select-final-attempt.ts +70 -0
  747. package/src/contract/trace/snapshot-html-renderer.ts +136 -0
  748. package/src/contract/trace/snapshot-resolver.ts +66 -0
  749. package/src/contract/trace/snapshot-store.ts +151 -0
  750. package/src/contract/trace/target-identity.ts +104 -0
  751. package/src/contract/trace/test-step-join.ts +104 -0
  752. package/src/contract/trace/trace-binding.ts +56 -0
  753. package/src/contract/trace/trace-file-reader.ts +133 -0
  754. package/src/contract/trace/trace-fingerprint.ts +68 -0
  755. package/src/contract/trace/trace-identity-resolution.ts +74 -0
  756. package/src/contract/trace/trace-run-record.ts +80 -0
  757. package/src/contract/trace/trace-steps.ts +88 -0
  758. package/src/contract/trace/trace-zip-limits.ts +77 -0
  759. package/src/contract/trace/trace-zip-reader.ts +127 -0
  760. package/src/contract/verify-approval-decisions.ts +60 -0
  761. package/src/decisions/append-decision.ts +35 -0
  762. package/src/decisions/confirmed-decision-commands.ts +149 -0
  763. package/src/decisions/decision-file.ts +93 -0
  764. package/src/decisions/decision-identity.ts +73 -0
  765. package/src/decisions/decision-integrity-check.ts +61 -0
  766. package/src/decisions/decision-schema.ts +76 -0
  767. package/src/decisions/decision-source.ts +14 -0
  768. package/src/decisions/exclusive-file-create.ts +42 -0
  769. package/src/decisions/git-head-trust.ts +32 -0
  770. package/src/decisions/human-confirmation.ts +119 -0
  771. package/src/decisions/legacy-waiver-migration.ts +135 -0
  772. package/src/decisions/read-decisions.ts +61 -0
  773. package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
  774. package/src/decisions/waiver-decisions.ts +56 -0
  775. package/src/exporters/csv-exporter.ts +5 -1
  776. package/src/exporters/feature-parser.ts +12 -0
  777. package/src/exporters/matrix/build.ts +62 -18
  778. package/src/exporters/matrix/carry-manual-results.ts +70 -7
  779. package/src/exporters/matrix/export.ts +17 -2
  780. package/src/exporters/matrix/gates.ts +62 -129
  781. package/src/exporters/matrix/render-csv.ts +4 -1
  782. package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
  783. package/src/exporters/matrix/render-xlsx.ts +6 -0
  784. package/src/exporters/matrix/types.ts +9 -1
  785. package/src/exporters/matrix/xlsx-cells.ts +1 -1
  786. package/src/exporters/playwright-report-parser.ts +1 -0
  787. package/src/exporters/scenario-merger.ts +2 -1
  788. package/src/exporters/types.ts +5 -0
  789. package/src/generators/test-generator/code-generator.ts +13 -0
  790. package/src/graph/evaluate-graph.ts +66 -0
  791. package/src/graph/locate-finding.ts +93 -0
  792. package/src/graph/node-decl.ts +72 -0
  793. package/src/graph/nodes/core-node-catalog.ts +15 -0
  794. package/src/graph/nodes/test-case-contract-nodes.ts +74 -0
  795. package/src/graph/nodes/test-case-nodes.ts +68 -0
  796. package/src/graph/nodes/unit-audit-nodes.ts +95 -0
  797. package/src/graph/nodes/unit-contract-node.ts +89 -0
  798. package/src/graph/nodes/unit-design-nodes.ts +89 -0
  799. package/src/graph/nodes/unit-execution-nodes.ts +77 -0
  800. package/src/graph/test-case-identity.ts +75 -0
  801. package/src/graph/test-case-results.ts +90 -0
  802. package/src/graph/test-case-satisfied.ts +55 -0
  803. package/src/graph/test-case-state-shape.ts +62 -0
  804. package/src/graph/test-case-states.ts +84 -0
  805. package/src/graph/unit-artifact-probes.ts +77 -0
  806. package/src/graph/unit-state-facts.ts +112 -0
  807. package/src/harness/audit.ts +46 -11
  808. package/src/harness/catalog/gate-rules.yaml +647 -0
  809. package/src/harness/counted-assertions.ts +33 -0
  810. package/src/harness/defect-draft.ts +115 -0
  811. package/src/harness/defects.ts +94 -0
  812. package/src/harness/gates/audit-gate-terms.ts +80 -0
  813. package/src/harness/gates/audit-gate-verdict.ts +78 -0
  814. package/src/harness/gates/audit-input-hash.ts +55 -0
  815. package/src/harness/gates/audit-report-verification.ts +201 -0
  816. package/src/harness/gates/decisions-hash.ts +42 -0
  817. package/src/harness/gates/delivery-checklist.ts +120 -0
  818. package/src/harness/gates/delivery-decision-gate.ts +139 -0
  819. package/src/harness/gates/delivery-decision-step.ts +89 -0
  820. package/src/harness/gates/gate-override-report.ts +114 -0
  821. package/src/harness/gates/gate-override-scope.ts +66 -0
  822. package/src/harness/gates/gate-override-validation.ts +75 -0
  823. package/src/harness/gates/gate-rule-catalog.ts +82 -0
  824. package/src/harness/gates/gate-terms.ts +59 -0
  825. package/src/harness/gates/in-run-decision-check.ts +99 -0
  826. package/src/harness/gates/journey-gate-inputs.ts +89 -0
  827. package/src/harness/gates/migrated-waiver-review.ts +68 -0
  828. package/src/harness/gates/resolve-rule.ts +94 -0
  829. package/src/harness/gates/unit-gate-overrides.ts +99 -0
  830. package/src/harness/journey.ts +215 -38
  831. package/src/harness/manifest.ts +69 -45
  832. package/src/harness/next-step.ts +60 -170
  833. package/src/harness/parse.ts +40 -13
  834. package/src/harness/provenance.ts +99 -11
  835. package/src/harness/script-check.ts +27 -6
  836. package/src/harness/sensors.ts +73 -47
  837. package/src/harness/triage-signals.ts +46 -0
  838. package/src/harness/triage-unit.ts +113 -0
  839. package/src/harness/triage.ts +127 -0
  840. package/src/harness/viewpoint-ledger.ts +2 -2
  841. package/src/hooks/bash-write-targets.ts +109 -0
  842. package/src/hooks/claude-hook-entry.ts +85 -0
  843. package/src/hooks/claude-transcript-turn.ts +96 -0
  844. package/src/hooks/guard-delivery-contract-rule.ts +110 -0
  845. package/src/hooks/guard-protected-paths.ts +71 -0
  846. package/src/hooks/guard-rules.ts +98 -0
  847. package/src/hooks/guard-tty-spoof-rule.ts +31 -0
  848. package/src/hooks/hand-back-core.ts +112 -0
  849. package/src/hooks/hook-cli-command.ts +17 -0
  850. package/src/hooks/hook-registry.ts +24 -0
  851. package/src/hooks/hook-result-contract.ts +114 -0
  852. package/src/hooks/hook-toggles.ts +66 -0
  853. package/src/hooks/pre-tool-guard-hook.ts +67 -0
  854. package/src/hooks/session-start-unit-state-hook.ts +56 -0
  855. package/src/hooks/stop-next-step-hook.ts +37 -0
  856. package/src/hooks/sungen-project-root.ts +80 -0
  857. package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
  858. package/src/orchestrator/ai-skills/commands.ts +22 -8
  859. package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
  860. package/src/orchestrator/assets-drift.ts +12 -5
  861. package/src/orchestrator/claude-hooks-install.ts +168 -0
  862. package/src/orchestrator/lockstep-version.ts +28 -15
  863. package/src/orchestrator/project-initializer.ts +22 -0
  864. package/src/orchestrator/screen-manager.ts +3 -1
  865. package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  866. package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  867. package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  868. package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  869. package/src/orchestrator/templates/ai-src/commands/create-test.md +14 -16
  870. package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  871. package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  872. package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
  873. package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  874. package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
  875. package/src/orchestrator/templates/ai-src/commands/review.md +2 -0
  876. package/src/orchestrator/templates/ai-src/commands/run-test.md +14 -10
  877. package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
  878. package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
  879. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  880. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  881. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  882. package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  883. package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  884. package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  885. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
  886. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
  887. package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
  888. package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  889. package/src/utils/safe-yaml-parse.ts +44 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Nothing sensitive goes into a lock (DOS-14.4, DOS-14.7, red-team #14). Before a lock is written:
3
+ *
4
+ * 1. the existing secret scanner (harness/secret-scan) runs over the EXACT text about to be
5
+ * written — vendor token prefixes, secret-named keys with long literals;
6
+ * 2. semantic detection by field meaning (exporters/sensitive-key's vocabulary — password, token,
7
+ * secret, otp, api key… — matched as whole words): a LITERAL typed into such a field.
8
+ * `{{var}}`, `<placeholder>` and `${ENV}` are references, not values, and pass.
9
+ *
10
+ * A hit refuses the write — nothing is written, neither the lock nor its decision — and names the
11
+ * place (feature file:line, or lock line) without echoing the value.
12
+ */
13
+ import * as fs from 'fs';
14
+ import * as os from 'os';
15
+ import * as path from 'path';
16
+ import { SENSITIVE_NAME } from '../exporters/sensitive-key';
17
+ import { scanTestDataSecrets } from '../harness/secret-scan';
18
+ import type { ContractScenario, ContractStep } from './contract-schema';
19
+ import { LOCK_FILE_NAME } from './contracts-lock-file';
20
+
21
+ export interface LockSecretHit { where: string; reason: string }
22
+
23
+ export class LockSecretError extends Error {
24
+ constructor(public readonly hits: LockSecretHit[]) {
25
+ super(`Refused: the contract would write ${hits.length} sensitive value(s) into ${LOCK_FILE_NAME}. Nothing was written.\n`
26
+ + hits.map((h) => ` ${h.where} — ${h.reason}`).join('\n')
27
+ + '\n Move each value into the unit\'s test-data (qa/<kind>/<unit>/test-data/*.yaml, or an env overlay for a real'
28
+ + ' credential), reference it in the step as {{variable}}, then approve again.');
29
+ this.name = 'LockSecretError';
30
+ }
31
+ }
32
+
33
+ const REFERENCE = /^(?:\{\{[^}]*\}\}|<[^>]+>|\$\{[A-Za-z_][A-Za-z0-9_]*\})$/;
34
+ const literals = (joined: string | undefined): string[] =>
35
+ (joined ?? '').split(' | ').map((v) => v.trim()).filter((v) => v.length > 0 && !REFERENCE.test(v));
36
+
37
+ /** The exporters' credential vocabulary, matched against WHOLE words (plural allowed), not substrings. */
38
+ const SENSITIVE_WORD = new RegExp(`^(?:${SENSITIVE_NAME.source})s?$`, 'i');
39
+
40
+ /**
41
+ * Is a field name a credential? Word by word — `confirmPassword`, `new_password`, `API Key`, `OTP` are;
42
+ * `Passenger Name`, `Compass`, `Bypass Code`, `Hotpot` are not (they only CONTAIN the letters).
43
+ * Adjacent words are also tried joined, so `API Key` reads as `apikey`.
44
+ */
45
+ export function isSensitiveFieldName(name: string | undefined): boolean {
46
+ if (!name) return false;
47
+ const words = name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
48
+ return words.some((w, i) => SENSITIVE_WORD.test(w) || (i > 0 && SENSITIVE_WORD.test(`${words[i - 1]}${w}`)));
49
+ }
50
+ const sensitive = isSensitiveFieldName;
51
+
52
+ function stepHit(step: ContractStep): string | null {
53
+ if (step.kind !== 'action') return null; // a check's expected text is what the page SHOWS, not a typed credential
54
+ const names = [step.target?.ref, ...(step.target?.also ?? [])];
55
+ const field = names.find(sensitive);
56
+ if (field && literals(step.value).length) return `a literal value typed into the sensitive field [${field}]`;
57
+ const [header, ...rows] = step.table ?? [];
58
+ const col = (header ?? []).findIndex(sensitive);
59
+ if (col >= 0 && rows.some((r) => literals(r[col]).length)) return `a literal value in the sensitive column "${header[col]}"`;
60
+ return null;
61
+ }
62
+
63
+ /** Semantic hits over the scenarios about to be locked. */
64
+ export function sensitiveLiteralHits(scenarios: ContractScenario[]): LockSecretHit[] {
65
+ const hits: LockSecretHit[] = [];
66
+ for (const sc of scenarios) {
67
+ sc.steps.forEach((step, i) => {
68
+ const reason = stepHit(step);
69
+ if (!reason) return;
70
+ const line = sc.source.lines[i];
71
+ hits.push({ where: `${path.basename(sc.source.file)}${line ? `:${line}` : ''} (${sc.id}, step ${i + 1})`, reason });
72
+ });
73
+ }
74
+ return hits;
75
+ }
76
+
77
+ /** The secret scanner over the lock text exactly as it would be written. */
78
+ export function scannerHits(lockText: string): LockSecretHit[] {
79
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sungen-lock-scan-'));
80
+ try {
81
+ fs.mkdirSync(path.join(dir, 'test-data'));
82
+ fs.writeFileSync(path.join(dir, 'test-data', LOCK_FILE_NAME), lockText);
83
+ const result = scanTestDataSecrets(dir);
84
+ if (result.unscanned.length) throw new Error(`the secret scan could not read the lock it was given (${result.unscanned[0].reason})`);
85
+ return result.hits.map((h) => ({ where: `${LOCK_FILE_NAME}:${h.line}`, reason: h.reason }));
86
+ } finally {
87
+ fs.rmSync(dir, { recursive: true, force: true });
88
+ }
89
+ }
90
+
91
+ /** Throws LockSecretError when the lock would carry a sensitive value. */
92
+ export function assertNoLockSecrets(scenarios: ContractScenario[], lockText: string): void {
93
+ const hits = [...sensitiveLiteralHits(scenarios), ...scannerHits(lockText)];
94
+ if (hits.length) throw new LockSecretError(hits);
95
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The working lock against the committed one (DOS-14.4, red-team #3). A lock entry is trusted only
3
+ * where it equals `git show HEAD:<lock>`: a commit went through review, a local edit did not — an
4
+ * agent that weakened a test could have edited the lock to match. Every difference is
5
+ * `contract.lock-uncommitted` (hard), including the cases that make a contract VANISH: an entry
6
+ * removed, or the whole lock deleted while HEAD still holds it.
7
+ *
8
+ * The same rule id carries the other ways a lock entry can fail to be what QA approved:
9
+ * tampered its steps no longer hash to its `hash` (contracts-lock-file.ts)
10
+ * unverified no committed contract-approval decision vouches for it (verify-approval-decisions.ts)
11
+ */
12
+ import type { ContractLock } from './contract-schema';
13
+ import { HeadSnapshot, headFileText, posixRel } from './contract-git';
14
+ import { sameJson } from './contract-hash';
15
+ import { parseLockText } from './contracts-lock-file';
16
+
17
+ export interface UncommittedEntry { id: string; change: 'added' | 'changed' | 'removed' | 'tampered' | 'unverified'; detail?: string }
18
+
19
+ export type HeadComparison = { status: 'no-git' } | { status: 'compared'; uncommitted: UncommittedEntry[] };
20
+
21
+ export function compareLockWithHead(
22
+ projectRoot: string, lockFile: string, workingText: string | null, working: ContractLock | null, snap: HeadSnapshot,
23
+ ): HeadComparison {
24
+ if (snap.blobs === null) return { status: 'no-git' };
25
+ const rel = posixRel(projectRoot, lockFile);
26
+ const headText = headFileText(snap, rel, workingText);
27
+ if (headText !== null && headText === workingText) return { status: 'compared', uncommitted: [] };
28
+ const head = headText === null ? null : parseLockText(headText, `HEAD:${rel}`);
29
+ const now = working?.scenarios ?? {};
30
+ const was = head?.scenarios ?? {};
31
+ const uncommitted: UncommittedEntry[] = [];
32
+ for (const id of [...new Set([...Object.keys(now), ...Object.keys(was)])].sort((a, b) => a.localeCompare(b, 'en'))) {
33
+ if (!(id in was)) uncommitted.push({ id, change: 'added' });
34
+ else if (!(id in now)) uncommitted.push({ id, change: 'removed' });
35
+ else if (!sameJson(now[id], was[id])) uncommitted.push({ id, change: 'changed' });
36
+ }
37
+ return { status: 'compared', uncommitted };
38
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * One parsed Gherkin step → one contract step (DOS-14.1). The step was already parsed by the
3
+ * existing GherkinParser (ParsedStep: parent scope, state clause, data table, nth); this re-reads
4
+ * its sentence with references and values masked, as the StepMapper's matcher does
5
+ * (`maskRefsForMatching`) so a NAME can never read as grammar — and keeps what it masked:
6
+ *
7
+ * `User see [Status] text with {{s}} in [Job Row] row with {{t}}`
8
+ * → {kind: assert, verb: 'see [] text with {}', target: {ref: 'Status', parent: 'row [Job Row] with {{t}}'},
9
+ * matcher: 'equals', expected: '{{s}}'}
10
+ *
11
+ * The verb keeps every word and number of the sentence, so two steps with equal contract steps are
12
+ * the same sentence up to case and spacing: an exact match is never a guess. `{{var}}` stays a
13
+ * variable (never resolved), a literal loses only its quotes.
14
+ */
15
+ import type { ParsedStep } from '../generators/gherkin-parser';
16
+ import { extractParentScopeClause } from '../generators/gherkin-parser/parent-scope-clause';
17
+ import type { ContractStep, ContractTarget } from './contract-schema';
18
+ import { assertShapeOf } from './assertion-matcher-vocabulary';
19
+
20
+ export type StepBucket = 'given' | 'when' | 'then';
21
+
22
+ /** Quoted literal · `{{var}}` · `<outline placeholder>` · `[Reference]`, left to right. */
23
+ const TOKEN = /"[^"]*"|\{\{[^}]*\}\}|<[^>\s][^>]*>|\[[^\]]*\]/g;
24
+
25
+ function parentOf(text: string): { parent?: string; body: string } {
26
+ const { clause } = extractParentScopeClause(text);
27
+ if (!clause) return { body: text };
28
+ const tail = clause.valueRef !== undefined ? ` with {{${clause.valueRef}}}` : clause.valueText !== undefined ? ` with "${clause.valueText}"` : '';
29
+ return { parent: `${clause.type} [${clause.ref.trim()}]${tail}`, body: clause.remainingText };
30
+ }
31
+
32
+ export function normalizeContractStep(step: ParsedStep, bucket: StepBucket): ContractStep {
33
+ const { parent, body } = parentOf(step.text);
34
+ const refs: string[] = [];
35
+ const values: string[] = [];
36
+ const masked = body.replace(TOKEN, (tok) => {
37
+ if (tok.startsWith('[')) { refs.push(tok.slice(1, -1).trim()); return '[]'; }
38
+ values.push(tok.startsWith('"') ? tok.slice(1, -1) : tok);
39
+ return '{}';
40
+ });
41
+ const verb = masked.replace(/^\s*user\s+/i, '').replace(/\s+/g, ' ').replace(/\s*[.:]\s*$/, '').trim().toLowerCase();
42
+ const kind = bucket === 'then' ? 'assert' : 'action';
43
+
44
+ let target: ContractTarget | undefined;
45
+ if (refs.length) target = { ref: refs[0], ...(refs.length > 1 ? { also: refs.slice(1) } : {}) };
46
+ else if (kind === 'assert' && /^expect\b/.test(verb) && values.length) target = { ref: values.shift()! };
47
+ if (target && parent) target.parent = parent;
48
+ if (target && step.nth && step.nth > 0) target.nth = step.nth;
49
+
50
+ const joined = values.length ? values.join(' | ') : undefined;
51
+ const table = step.dataTable ? [step.dataTable.headers, ...step.dataTable.rows.map((r) => r.cells)] : undefined;
52
+ const out: ContractStep = { kind, verb };
53
+ if (target) out.target = target;
54
+ if (kind === 'action') {
55
+ if (joined !== undefined) out.value = joined;
56
+ } else {
57
+ const shape = assertShapeOf(verb, step.state);
58
+ out.matcher = shape.matcher;
59
+ if (joined !== undefined) out.expected = joined;
60
+ if (shape.negated) out.negated = true;
61
+ }
62
+ if (table) out.table = table;
63
+ return out;
64
+ }
65
+
66
+ /** Given/When/Then for every step of one block, And/But inheriting the last primary keyword. */
67
+ export function bucketsOf(steps: ParsedStep[]): StepBucket[] {
68
+ let last: StepBucket = 'given';
69
+ return steps.map((s) => {
70
+ const kw = s.keyword.trim().toLowerCase();
71
+ if (kw === 'given' || kw === 'when' || kw === 'then') last = kw;
72
+ return last;
73
+ });
74
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * A unit's `.feature` files → its contract scenarios (DOS-14.1, 14.2), through the existing
3
+ * GherkinParser — no second parser.
4
+ *
5
+ * which scenarios every automated one: not `@manual` (scenario or feature), not `@deferred:flow`,
6
+ * not a `@steps:` block or a `@beforeAll/@afterEach/@afterAll` hook
7
+ * steps Background + the `@extend:` base's Given/When (the compiler's own rule) + its own
8
+ * id the title's leading ID (`VP-LOGIN-001`), as the harness reads it; a title with no
9
+ * ID is locked by its name, so renaming it reads as removing it
10
+ * implementation `@free` → free, `@impl:code` → impl:code, otherwise compiled — scenario or feature tag
11
+ * hash contractHash(implementation, steps); the scenario name is not in it
12
+ *
13
+ * A file that does not parse throws: an unreadable contract is never an empty one.
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import { GherkinParser, ParsedScenario, ParsedStep } from '../generators/gherkin-parser';
18
+ import { isIdLike } from '../harness/parse';
19
+ import { featureFilesFor } from '../harness/unit-paths';
20
+ import { contractHash } from './contract-hash';
21
+ import type { ContractScenario, ContractStep, Implementation } from './contract-schema';
22
+ import { bucketsOf, normalizeContractStep } from './normalize-contract-step';
23
+
24
+ const EXCLUDED = (tags: string[]): boolean => tags.some((t) => /^@manual\b/i.test(t) || /^@deferred:flow$/i.test(t));
25
+
26
+ function implementationOf(tags: string[]): Implementation {
27
+ if (tags.some((t) => /^@free$/i.test(t))) return 'free';
28
+ if (tags.some((t) => /^@impl:code$/i.test(t))) return 'impl:code';
29
+ return 'compiled';
30
+ }
31
+
32
+ function idOf(name: string): { id: string; hasId: boolean } {
33
+ const lead = name.match(/^\s*([A-Za-z][A-Za-z0-9.-]*)/)?.[1];
34
+ return lead && isIdLike(lead) ? { id: lead.toUpperCase(), hasId: true } : { id: name.replace(/\s+/g, ' ').trim(), hasId: false };
35
+ }
36
+
37
+ /** Only Given→When of an `@extend` base: its Then (and the And/But after it) stay with the base. */
38
+ const extendPart = (steps: ParsedStep[]): ParsedStep[] => steps.filter((_, i) => bucketsOf(steps)[i] !== 'then');
39
+
40
+ const normalizeBlock = (steps: ParsedStep[]): ContractStep[] => {
41
+ const buckets = bucketsOf(steps);
42
+ return steps.map((s, i) => normalizeContractStep(s, buckets[i]));
43
+ };
44
+
45
+ /** 1-based line of each step, searched in order from line index `from` — for messages only (0 = not found). */
46
+ function stepLines(lines: string[], from: number, steps: ParsedStep[]): number[] {
47
+ let at = Math.max(from, 0);
48
+ return steps.map((s) => {
49
+ const i = lines.findIndex((l, n) => n >= at && l.trim().endsWith(s.text.trim()));
50
+ if (i >= 0) at = i + 1;
51
+ return i + 1;
52
+ });
53
+ }
54
+
55
+ const HEADER = /^\s*(?:Scenario|Scenario Outline|Scenario Template|Example):/;
56
+ const OUTLINE = /^\s*Scenario (?:Outline|Template):/;
57
+ const headerLine = (lines: string[], name: string): number => lines.findIndex((l) => HEADER.test(l) && l.includes(name.trim()));
58
+
59
+ export function scenariosOfFeatureFile(file: string): ContractScenario[] {
60
+ const feature = new GherkinParser().parseFeatureFile(file);
61
+ const lines = fs.readFileSync(file, 'utf8').split(/\r?\n/);
62
+ const base = new Map<string, ParsedScenario>();
63
+ for (const sc of feature.scenarios) if (sc.stepsName) base.set(sc.stepsName, sc);
64
+ const background = feature.background?.steps ?? [];
65
+ const out: ContractScenario[] = [];
66
+ for (const sc of feature.scenarios) {
67
+ const tags = [...(feature.tags ?? []), ...(sc.tags ?? [])];
68
+ if (sc.stepsName || sc.hookType || EXCLUDED(tags)) continue;
69
+ const baseScenario = sc.extendsName ? base.get(sc.extendsName) : undefined;
70
+ const extended = extendPart(baseScenario?.steps ?? []);
71
+ const steps = [...normalizeBlock(background), ...normalizeBlock(extended), ...normalizeBlock(sc.steps)];
72
+ const implementation = implementationOf(tags);
73
+ const { id, hasId } = idOf(sc.name);
74
+ const header = headerLine(lines, sc.name);
75
+ const sourceLines = [
76
+ ...stepLines(lines, 0, background),
77
+ ...stepLines(lines, baseScenario ? headerLine(lines, baseScenario.name) : 0, extended),
78
+ ...stepLines(lines, header, sc.steps),
79
+ ];
80
+ const outline = header >= 0 && OUTLINE.test(lines[header]);
81
+ out.push({ id, implementation, steps, hash: contractHash(implementation, steps), source: { file, name: sc.name, hasId, lines: sourceLines, outline } });
82
+ }
83
+ return out;
84
+ }
85
+
86
+ /** Every contract scenario of a unit, in `featureFilesFor` order. Missing feature files contribute nothing. */
87
+ export function loadContractScenarios(unitDir: string, unit: string): ContractScenario[] {
88
+ return featureFilesFor(unitDir, unit).filter((f) => fs.existsSync(f)).flatMap(scenariosOfFeatureFile);
89
+ }
90
+
91
+ /** Feature files that make up a unit's contract input (for the inputs hash). */
92
+ export const contractFeatureFiles = (unitDir: string, unit: string): string[] =>
93
+ featureFilesFor(unitDir, unit).filter((f) => fs.existsSync(f)).map((f) => path.resolve(f));
@@ -0,0 +1,80 @@
1
+ /**
2
+ * How a changed assertion compares to the approved one on the same target (DOS-14.4).
3
+ *
4
+ * Every rule is written for POSITIVE checks, from the approved check A to the new check B:
5
+ * weakened expected value dropped · a data-table row removed or a cell changed · a shorter
6
+ * literal under `contains` · a lower rung on the ladder (conservative)
7
+ * strengthened B implies A, with the same expected value, on the same element (IMPLICATION, never
8
+ * rung): equals ⇒ contains · all:X ⇒ X · url ⇒ url-contains · title ⇒ title-contains
9
+ * review anything else — it blocks delivery until QA re-approves it
10
+ * Nothing implies `visible`: a text or count check passes on a hidden element, and a state (enabled,
11
+ * checked, empty, focused, selected, loading, sorted…) says nothing about visibility either.
12
+ *
13
+ * Under a NEGATION the direction reverses — `not P` proves `not Q` exactly when Q ⇒ P — so the same
14
+ * rules run with A and B swapped: `does not see [Toast]` → `does not see [Toast] with text {{x}}` is
15
+ * a weakening (a visible toast with other text now passes). A negation added or removed is a flip.
16
+ */
17
+ import { sameJson } from './contract-hash';
18
+ import type { ContractStep } from './contract-schema';
19
+ import { matcherStrength } from './assertion-matcher-vocabulary';
20
+
21
+ export { assertShapeOf, matcherStrength } from './assertion-matcher-vocabulary';
22
+ export type { AssertShape } from './assertion-matcher-vocabulary';
23
+
24
+ const IMPLIES: Record<string, readonly string[]> = { equals: ['contains'], url: ['url-contains'], title: ['title-contains'] };
25
+
26
+ /** Does a passing `now` check prove the `was` check (both positive)? */
27
+ export function impliesMatcher(now: string | undefined, was: string | undefined): boolean {
28
+ if (!now || !was || now === was) return false;
29
+ if (was.startsWith('all:') && !now.startsWith('all:')) return false;
30
+ const n = now.replace(/^all:/, '');
31
+ const w = was.replace(/^all:/, '');
32
+ return n === w || (IMPLIES[n] ?? []).includes(w);
33
+ }
34
+
35
+ const CONNECTORS = new Set(['with', 'has', 'have', 'contains', 'contain', 'includes', 'include', 'is', 'matches', 'in', 'to', 'and']);
36
+ const elementType = (verb: string): string => {
37
+ const m = verb.match(/\[\] ([\w-]+)/);
38
+ return m && !CONNECTORS.has(m[1]) ? m[1] : '';
39
+ };
40
+ const isLiteral = (v: string | undefined): v is string => v !== undefined && !/\{\{|<[^>]+>/.test(v);
41
+ const baseOf = (m: string | undefined): string => (m ?? '').replace(/^all:/, '');
42
+ const rowsOf = (t: string[][] | undefined): string[] => (t ?? []).map((r) => JSON.stringify(r));
43
+ const TEXT = new Set(['equals', 'contains']);
44
+
45
+ /** x ⇒ y for two positive LITERAL text checks (equals / contains); null when they are not both that. */
46
+ function literalImplies(x: ContractStep, y: ContractStep): boolean | null {
47
+ const bx = baseOf(x.matcher);
48
+ const by = baseOf(y.matcher);
49
+ if (!isLiteral(x.expected) || !isLiteral(y.expected) || !TEXT.has(bx) || !TEXT.has(by)) return null;
50
+ return by === 'equals' ? bx === 'equals' && x.expected === y.expected : x.expected.includes(y.expected);
51
+ }
52
+
53
+ export type AssertComparison = { class: 'strengthened' | 'review' | 'oracle-weakened'; detail: string };
54
+
55
+ const show = (s: ContractStep): string => `${s.negated ? 'not ' : ''}${s.matcher ?? '?'}${s.expected !== undefined ? ` "${s.expected}"` : ''}`;
56
+
57
+ /** Two assertions on the same target that are NOT identical. */
58
+ export function compareAsserts(locked: ContractStep, current: ContractStep): AssertComparison {
59
+ const change = `${show(locked)} → ${show(current)}`;
60
+ const weak = (why: string): AssertComparison => ({ class: 'oracle-weakened', detail: `${why}: ${change}` });
61
+ const review = (why: string): AssertComparison => ({ class: 'review', detail: `${why}: ${change}` });
62
+ if (!!locked.negated !== !!current.negated) return weak('negation flipped');
63
+ const neg = locked.negated === true;
64
+ const [a, b] = neg ? [current, locked] : [locked, current];
65
+ const sa = matcherStrength(a.matcher);
66
+ const sb = matcherStrength(b.matcher);
67
+ if (sa === null || sb === null) return review(`the ladder cannot judge "${locked.verb}" → "${current.verb}"`);
68
+ if (a.expected !== undefined && b.expected === undefined) return weak(neg ? 'a value was added to a negated check' : 'expected value dropped');
69
+ if (a.table) {
70
+ const kept = new Set(rowsOf(b.table));
71
+ const lost = rowsOf(a.table).filter((r) => !kept.has(r)).length;
72
+ if (lost) return weak(`${lost} data-table row(s) ${neg ? 'added to a negated check' : 'removed or changed'}`);
73
+ }
74
+ if (literalImplies(a, b) === true && literalImplies(b, a) === false) return weak(neg ? 'a longer literal under a negated check' : 'a shorter literal under contains');
75
+ if (sb < sa) return weak('weaker check');
76
+ if (a.expected !== undefined && b.expected !== a.expected) return review('expected value changed');
77
+ const sameElement = sameJson(a.target, b.target) && elementType(a.verb) === elementType(b.verb) && rowsOf(a.table).length === rowsOf(b.table).length;
78
+ if (sameElement && impliesMatcher(b.matcher, a.matcher)) return { class: 'strengthened', detail: `stronger check: ${change}` };
79
+ return review('changed, and the new check does not prove the approved one');
80
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Which units' contracts hold a delivery back — for the Claude Code guard (OES-12.3, DOS-14.4).
3
+ *
4
+ * HEAD is read once (one `git ls-tree`, bounded by the caller's timeout). A unit with no lock in the
5
+ * working tree AND none in HEAD has no contract to break and is skipped without parsing anything.
6
+ * Every other unit gets its verdict rebuilt by buildContractVerdict — the same function `contract
7
+ * check` runs — and anything but `pass` blocks. A lock deleted from the working tree still counts
8
+ * while HEAD holds it. If git FAILS in a checkout that has .git while a delivered unit has a lock,
9
+ * this throws (GitUnavailableError): the guard asks rather than calling an unverifiable lock clean.
10
+ */
11
+ import * as fs from 'fs';
12
+ import { buildContractVerdict, ContractUnit, VerdictValue } from './build-contract-verdict';
13
+ import { CLI_GIT_TIMEOUT_MS, GitUnavailableError, headSnapshot, posixRel } from './contract-git';
14
+ import { HARD_CLASSES } from './contract-schema';
15
+ import { lockPathFor } from './contracts-lock-file';
16
+
17
+ export interface ContractBlocker { unit: string; verdict: Exclude<VerdictValue, 'pass'>; headline: string }
18
+
19
+ export function contractBlockers(projectRoot: string, units: ContractUnit[], opts: { gitTimeoutMs?: number } = {}): ContractBlocker[] {
20
+ const unique = [...new Map(units.map((u) => [u.dir, u])).values()];
21
+ const snap = headSnapshot(projectRoot, opts.gitTimeoutMs ?? CLI_GIT_TIMEOUT_MS);
22
+ const hasWorkingLock = (u: ContractUnit): boolean => fs.existsSync(lockPathFor(u.dir));
23
+ if (snap.error && unique.some(hasWorkingLock)) throw new GitUnavailableError(snap.error);
24
+ const out: ContractBlocker[] = [];
25
+ for (const unit of unique) {
26
+ if (!hasWorkingLock(unit) && !snap.blobs?.has(posixRel(projectRoot, lockPathFor(unit.dir)))) continue;
27
+ const v = buildContractVerdict(projectRoot, unit, { snapshot: snap });
28
+ if (v.verdict === 'pass') continue;
29
+ const worst = v.scenarios.find((s) => HARD_CLASSES.has(s.class)) ?? v.scenarios.find((s) => s.class === 'hash-changed');
30
+ const headline = worst
31
+ ? `${worst.id} ${worst.class}${worst.findings[0] ? ` (${worst.findings[0].detail})` : ''}`
32
+ : `${v.uncommitted.length} lock entr${v.uncommitted.length === 1 ? 'y is' : 'ies are'} not what QA approved and committed (${v.uncommitted[0]?.id} ${v.uncommitted[0]?.change})`;
33
+ out.push({ unit: unit.name, verdict: v.verdict, headline });
34
+ }
35
+ return out;
36
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * What an approval writes, once a human has confirmed it (DOS-14.3, DOS-14.4, takumi T5):
3
+ *
4
+ * 1. the secret guard over the exact lock text — a hit writes nothing at all
5
+ * 2. a `contract-approval` decision listing every approved scenario id + hash (and the removals)
6
+ * 3. the lock: each approved entry `humanApproved: true`, `decisionId` = that decision, and
7
+ * `approvedAt` = its `valid_from` — the pair `contract check` cross-checks (M1)
8
+ *
9
+ * The decision goes first: a crash between the two leaves a record without a lock entry, never an
10
+ * entry pointing at nothing. Only `sungen contract approve` calls this after its TTY confirmation;
11
+ * the confirmation itself lives there (approve-contract.ts).
12
+ */
13
+ import * as path from 'path';
14
+ import { appendDecision } from '../decisions/append-decision';
15
+ import { isValidScope } from '../decisions/decision-file';
16
+ import type { JsonValue } from '../decisions/decision-schema';
17
+ import { gitUserName } from '../decisions/decision-source';
18
+ import { ContractLock, ContractScenario, LOCK_SCHEMA } from './contract-schema';
19
+ import { lockEntryFor, lockPathFor, serializeLock, writeLockFile } from './contracts-lock-file';
20
+ import { assertNoLockSecrets } from './lock-secret-guard';
21
+
22
+ export interface ApprovalWrite {
23
+ projectRoot: string;
24
+ unitDir: string;
25
+ unit: string;
26
+ approve: ContractScenario[];
27
+ removed: string[];
28
+ reason?: string;
29
+ lock: ContractLock | null;
30
+ }
31
+
32
+ export const DEFAULT_APPROVAL_REASON = 'QA reviewed the normalized Gherkin contract';
33
+
34
+ /** The decision scope of a unit directory (`screens/login`, `api/flows/pay`…). */
35
+ export function scopeOfUnitDir(projectRoot: string, unitDir: string): string {
36
+ const scope = path.relative(path.join(projectRoot, 'qa'), unitDir).split(path.sep).join('/');
37
+ if (!isValidScope(scope)) throw new Error(`${unitDir} is not a unit directory under qa/ (screens|flows|api/<unit>, api/flows/<flow>)`);
38
+ return scope;
39
+ }
40
+
41
+ export function nextLock(w: ApprovalWrite, decisionId: string, approvedAt: string): ContractLock {
42
+ const scenarios = { ...(w.lock?.scenarios ?? {}) };
43
+ for (const id of w.removed) delete scenarios[id];
44
+ for (const sc of w.approve) scenarios[sc.id] = lockEntryFor(sc, decisionId, approvedAt);
45
+ return { schema: LOCK_SCHEMA, scenarios };
46
+ }
47
+
48
+ /** Throws LockSecretError when the lock this approval would write carries a sensitive value. */
49
+ export function checkApprovalSecrets(w: ApprovalWrite): void {
50
+ assertNoLockSecrets(w.approve, serializeLock(nextLock(w, 'D-0000000000000000', new Date().toISOString())));
51
+ }
52
+
53
+ export function recordContractApproval(w: ApprovalWrite): { decisionId: string; lock: ContractLock } {
54
+ const scope = scopeOfUnitDir(w.projectRoot, w.unitDir);
55
+ checkApprovalSecrets(w);
56
+ const payload: Record<string, JsonValue> = {
57
+ unit: w.unit,
58
+ scenarios: w.approve.map((s) => ({ id: s.id, hash: s.hash, implementation: s.implementation })),
59
+ removed: w.removed,
60
+ };
61
+ const decision = appendDecision(w.projectRoot, scope, {
62
+ kind: 'contract-approval', anchors: [], visibility: 'internal', valid_from: new Date().toISOString(), payload,
63
+ text: `Approved the Test Contract of ${w.unit}: ${[...w.approve.map((s) => s.id), ...w.removed.map((id) => `-${id}`)].join(', ')}`,
64
+ reason: w.reason?.trim() || DEFAULT_APPROVAL_REASON,
65
+ source: { by: gitUserName(w.projectRoot), where: 'cli' },
66
+ });
67
+ const lock = nextLock(w, decision.entry.id, decision.entry.valid_from);
68
+ writeLockFile(lockPathFor(w.unitDir), lock);
69
+ return { decisionId: decision.entry.id, lock };
70
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * A trace's intent steps → Gherkin steps, deterministically (DOS-14.8): the same trace and the same
3
+ * identities give the same text, byte for byte. No LLM is involved — naming a scenario or suggesting
4
+ * a VP-id is left to the user (or an agent) and never touches the steps or the hash.
5
+ *
6
+ * keywords first step `Given`; the next action `When`, later actions `And`; a check after an
7
+ * action `Then`, after a check `And`; an action after a check `When`
8
+ * drops a click on an element immediately followed by a fill of the SAME element (a focus click)
9
+ * opaque `User see opaque check "<source line>"` / `User do opaque step "<GET /api/x>"`, with a
10
+ * `# opaque at <file>:<line>` comment — kept verbatim, always reviewed by a human
11
+ * low `# identity low: <why>` above any step whose target is not proven
12
+ * safety secrets typed into credential fields are redacted everywhere; machine paths scrubbed
13
+ */
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import { displayPath, scrubKnownSecrets, scrubMachinePaths } from './field-semantic-redaction';
17
+ import { normalizeShownText, TestDataIndex } from './data-normalize';
18
+ import { renderTarget, sameTarget } from './render-trace-target';
19
+ import { actionSentence, assertSentence, sensitiveStep } from './render-trace-sentence';
20
+ import type { TargetFacts, TargetIdentity } from './trace/target-identity';
21
+ import { isPageLevel } from './trace/trace-identity-resolution';
22
+ import type { IntentStep } from './trace/trace-steps';
23
+
24
+ export const CONTROL_CHARS = /[\u0000-\u001f\u007f\u2028\u2029]/g;
25
+
26
+ export interface ResolvedTarget { identity: TargetIdentity; facts?: TargetFacts }
27
+ export interface RenderedStep {
28
+ keyword: 'Given' | 'When' | 'Then' | 'And';
29
+ sentence: string;
30
+ kind: 'action' | 'assert';
31
+ /** The outermost `test.step` title (impl:code compares group by group). */
32
+ group?: string;
33
+ low?: string;
34
+ opaque?: { verbatim: string; where?: string };
35
+ }
36
+ export interface RenderContext { index: TestDataIndex; projectRoot: string; specFile?: string; secrets: Set<string> }
37
+
38
+ function whereOf(step: IntentStep, ctx: RenderContext): { where?: string; line?: string } {
39
+ const loc = step.location;
40
+ if (!loc?.file) return {};
41
+ const own = ctx.specFile && (loc.file === ctx.specFile || loc.file.endsWith(`/${ctx.specFile}`) || path.basename(loc.file) === path.basename(ctx.specFile));
42
+ const file = own ? ctx.specFile! : displayPath(loc.file, ctx.projectRoot);
43
+ let line: string | undefined;
44
+ if (own) {
45
+ try { line = fs.readFileSync(path.join(ctx.projectRoot, ctx.specFile!), 'utf8').split(/\r?\n/)[loc.line - 1]?.trim(); } catch { line = undefined; }
46
+ }
47
+ return { where: `${file}:${loc.line}`, line };
48
+ }
49
+
50
+ const quoteVerbatim = (s: string): string => `"${normalizeShownText(s).replace(/"/g, "'").replace(/\s+/g, ' ').trim()}"`;
51
+
52
+ function keywordFor(kind: 'action' | 'assert', prev: RenderedStep[]): RenderedStep['keyword'] {
53
+ const last = prev[prev.length - 1];
54
+ if (!last) return 'Given';
55
+ if (kind === 'assert') return last.kind === 'assert' ? 'And' : 'Then';
56
+ if (last.kind === 'assert') return 'When';
57
+ return prev.length === 1 && last.keyword === 'Given' ? 'When' : 'And';
58
+ }
59
+
60
+ export function renderTraceSteps(steps: IntentStep[], targets: Map<string, ResolvedTarget>, ctx: RenderContext): RenderedStep[] {
61
+ const out: RenderedStep[] = [];
62
+ steps.forEach((step, i) => {
63
+ const t = step.callId ? targets.get(step.callId) : undefined;
64
+ const next = steps[i + 1];
65
+ if (step.method === 'click' && next && (next.method === 'fill' || next.method === 'type') && sameTarget(t?.identity, next.callId ? targets.get(next.callId)?.identity : undefined)) return;
66
+ const group = step.group[0];
67
+ if (step.kind === 'opaque-assert' || step.kind === 'opaque-action') {
68
+ const { where, line } = whereOf(step, ctx);
69
+ const verbatim = step.kind === 'opaque-action' && /^[A-Z]+ /.test(step.verbatim ?? '') ? step.verbatim! : line || step.verbatim || step.method;
70
+ const kind = step.kind === 'opaque-assert' ? 'assert' : 'action';
71
+ const sentence = kind === 'assert' ? `User see opaque check ${quoteVerbatim(verbatim)}` : `User do opaque step ${quoteVerbatim(verbatim)}`;
72
+ out.push({ keyword: keywordFor(kind, out), sentence, kind, ...(group ? { group } : {}), opaque: { verbatim, ...(where ? { where } : {}) } });
73
+ return;
74
+ }
75
+ const target = isPageLevel(step) ? { text: '' } : renderTarget(t?.identity, t?.facts, typeof step.params.selector === 'string' ? step.params.selector : undefined);
76
+ const kind = step.kind === 'assert' ? 'assert' : 'action';
77
+ const sentence = kind === 'assert' ? assertSentence(step, target.text) : actionSentence(step, target.text, ctx, sensitiveStep(step, t?.facts, t?.identity.name));
78
+ out.push({ keyword: keywordFor(kind, out), sentence, kind, ...(group ? { group } : {}), ...(target.low ? { low: target.low } : {}) });
79
+ });
80
+ // Control characters (terminal escapes, BEL, line separators) would corrupt or spoof a Gherkin line.
81
+ const clean = (s: string): string => scrubMachinePaths(scrubKnownSecrets(s, ctx.secrets), ctx.projectRoot).replace(CONTROL_CHARS, '');
82
+ return out.map((s) => ({
83
+ ...s, sentence: clean(s.sentence), ...(s.low ? { low: clean(s.low) } : {}),
84
+ ...(s.opaque ? { opaque: { verbatim: clean(s.opaque.verbatim), ...(s.opaque.where ? { where: clean(s.opaque.where) } : {}) } } : {}),
85
+ }));
86
+ }
87
+
88
+ /** The Gherkin lines of one scenario's steps, with their comments. */
89
+ export function stepLines(steps: RenderedStep[]): string[] {
90
+ const lines: string[] = [];
91
+ for (const s of steps) {
92
+ if (s.opaque) lines.push(` # opaque${s.opaque.where ? ` at ${s.opaque.where}` : ''} — compared by a human, never automatically`);
93
+ if (s.low) lines.push(` # identity low: ${s.low}`);
94
+ lines.push(` ${s.keyword} ${s.sentence}`);
95
+ }
96
+ return lines;
97
+ }
98
+
99
+ export interface FreeScenarioBlock { id: string; title: string; tags: string[]; comments: string[]; steps: RenderedStep[] }
100
+
101
+ export function scenarioBlockText(b: FreeScenarioBlock): string {
102
+ return [` ${b.tags.join(' ')}`, ` Scenario: ${b.title}`, ...b.comments.map((c) => ` # ${c}`), ...stepLines(b.steps)].join('\n');
103
+ }
104
+
105
+ export const FREE_FEATURE_HEADER = [
106
+ '# Derived by `sungen contract derive` from Playwright traces — deterministic, no LLM.',
107
+ '# These steps are the contract; the Playwright test is the implementation. Each scenario stays a',
108
+ '# proposal (not counted by the audit) until QA approves it: sungen contract approve --screen <unit>',
109
+ ];
110
+
111
+ export function freeFeatureText(unit: string, blocks: string[]): string {
112
+ return [...FREE_FEATURE_HEADER.map((l) => l.replace('<unit>', unit)), `Feature: ${unit} (free tests)`, '', ...blocks.flatMap((b) => [b, ''])].join('\n');
113
+ }