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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (866) 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/registry.d.ts +15 -0
  34. package/dist/capabilities/registry.d.ts.map +1 -1
  35. package/dist/capabilities/registry.js.map +1 -1
  36. package/dist/cli/commands/audit.d.ts.map +1 -1
  37. package/dist/cli/commands/audit.js +9 -4
  38. package/dist/cli/commands/audit.js.map +1 -1
  39. package/dist/cli/commands/capability.d.ts.map +1 -1
  40. package/dist/cli/commands/capability.js +6 -2
  41. package/dist/cli/commands/capability.js.map +1 -1
  42. package/dist/cli/commands/contract-derive-command.d.ts +10 -0
  43. package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
  44. package/dist/cli/commands/contract-derive-command.js +99 -0
  45. package/dist/cli/commands/contract-derive-command.js.map +1 -0
  46. package/dist/cli/commands/contract.d.ts +17 -0
  47. package/dist/cli/commands/contract.d.ts.map +1 -0
  48. package/dist/cli/commands/contract.js +162 -0
  49. package/dist/cli/commands/contract.js.map +1 -0
  50. package/dist/cli/commands/defect.d.ts +7 -0
  51. package/dist/cli/commands/defect.d.ts.map +1 -0
  52. package/dist/cli/commands/defect.js +68 -0
  53. package/dist/cli/commands/defect.js.map +1 -0
  54. package/dist/cli/commands/delivery.d.ts.map +1 -1
  55. package/dist/cli/commands/delivery.js +87 -10
  56. package/dist/cli/commands/delivery.js.map +1 -1
  57. package/dist/cli/commands/generate.d.ts.map +1 -1
  58. package/dist/cli/commands/generate.js +3 -1
  59. package/dist/cli/commands/generate.js.map +1 -1
  60. package/dist/cli/commands/journey.d.ts.map +1 -1
  61. package/dist/cli/commands/journey.js +41 -13
  62. package/dist/cli/commands/journey.js.map +1 -1
  63. package/dist/cli/commands/next.d.ts +8 -0
  64. package/dist/cli/commands/next.d.ts.map +1 -1
  65. package/dist/cli/commands/next.js +64 -6
  66. package/dist/cli/commands/next.js.map +1 -1
  67. package/dist/cli/commands/triage.d.ts +7 -0
  68. package/dist/cli/commands/triage.d.ts.map +1 -0
  69. package/dist/cli/commands/triage.js +98 -0
  70. package/dist/cli/commands/triage.js.map +1 -0
  71. package/dist/cli/commands/update.d.ts +11 -0
  72. package/dist/cli/commands/update.d.ts.map +1 -1
  73. package/dist/cli/commands/update.js +31 -6
  74. package/dist/cli/commands/update.js.map +1 -1
  75. package/dist/cli/index.js +24 -33
  76. package/dist/cli/index.js.map +1 -1
  77. package/dist/contract/approve-contract.d.ts +31 -0
  78. package/dist/contract/approve-contract.d.ts.map +1 -0
  79. package/dist/contract/approve-contract.js +87 -0
  80. package/dist/contract/approve-contract.js.map +1 -0
  81. package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
  82. package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
  83. package/dist/contract/assertion-matcher-vocabulary.js +100 -0
  84. package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
  85. package/dist/contract/build-contract-verdict.d.ts +32 -0
  86. package/dist/contract/build-contract-verdict.d.ts.map +1 -0
  87. package/dist/contract/build-contract-verdict.js +123 -0
  88. package/dist/contract/build-contract-verdict.js.map +1 -0
  89. package/dist/contract/build-trace-conformance.d.ts +18 -0
  90. package/dist/contract/build-trace-conformance.d.ts.map +1 -0
  91. package/dist/contract/build-trace-conformance.js +153 -0
  92. package/dist/contract/build-trace-conformance.js.map +1 -0
  93. package/dist/contract/classify-contract-change.d.ts +23 -0
  94. package/dist/contract/classify-contract-change.d.ts.map +1 -0
  95. package/dist/contract/classify-contract-change.js +98 -0
  96. package/dist/contract/classify-contract-change.js.map +1 -0
  97. package/dist/contract/classify-contract-unit.d.ts +18 -0
  98. package/dist/contract/classify-contract-unit.d.ts.map +1 -0
  99. package/dist/contract/classify-contract-unit.js +49 -0
  100. package/dist/contract/classify-contract-unit.js.map +1 -0
  101. package/dist/contract/classify-trace-conformance.d.ts +32 -0
  102. package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
  103. package/dist/contract/classify-trace-conformance.js +117 -0
  104. package/dist/contract/classify-trace-conformance.js.map +1 -0
  105. package/dist/contract/contract-git.d.ts +24 -0
  106. package/dist/contract/contract-git.d.ts.map +1 -0
  107. package/dist/contract/contract-git.js +144 -0
  108. package/dist/contract/contract-git.js.map +1 -0
  109. package/dist/contract/contract-hash.d.ts +7 -0
  110. package/dist/contract/contract-hash.d.ts.map +1 -0
  111. package/dist/contract/contract-hash.js +38 -0
  112. package/dist/contract/contract-hash.js.map +1 -0
  113. package/dist/contract/contract-report-file.d.ts +4 -0
  114. package/dist/contract/contract-report-file.d.ts.map +1 -0
  115. package/dist/contract/contract-report-file.js +60 -0
  116. package/dist/contract/contract-report-file.js.map +1 -0
  117. package/dist/contract/contract-schema.d.ts +71 -0
  118. package/dist/contract/contract-schema.d.ts.map +1 -0
  119. package/dist/contract/contract-schema.js +102 -0
  120. package/dist/contract/contract-schema.js.map +1 -0
  121. package/dist/contract/contract-units.d.ts +12 -0
  122. package/dist/contract/contract-units.d.ts.map +1 -0
  123. package/dist/contract/contract-units.js +96 -0
  124. package/dist/contract/contract-units.js.map +1 -0
  125. package/dist/contract/contracts-lock-file.d.ts +24 -0
  126. package/dist/contract/contracts-lock-file.d.ts.map +1 -0
  127. package/dist/contract/contracts-lock-file.js +140 -0
  128. package/dist/contract/contracts-lock-file.js.map +1 -0
  129. package/dist/contract/data-normalize.d.ts +10 -0
  130. package/dist/contract/data-normalize.d.ts.map +1 -0
  131. package/dist/contract/data-normalize.js +110 -0
  132. package/dist/contract/data-normalize.js.map +1 -0
  133. package/dist/contract/derive-free-feature.d.ts +30 -0
  134. package/dist/contract/derive-free-feature.d.ts.map +1 -0
  135. package/dist/contract/derive-free-feature.js +145 -0
  136. package/dist/contract/derive-free-feature.js.map +1 -0
  137. package/dist/contract/field-semantic-redaction.d.ts +21 -0
  138. package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
  139. package/dist/contract/field-semantic-redaction.js +118 -0
  140. package/dist/contract/field-semantic-redaction.js.map +1 -0
  141. package/dist/contract/free-feature-blocks.d.ts +10 -0
  142. package/dist/contract/free-feature-blocks.d.ts.map +1 -0
  143. package/dist/contract/free-feature-blocks.js +94 -0
  144. package/dist/contract/free-feature-blocks.js.map +1 -0
  145. package/dist/contract/lock-secret-guard.d.ts +22 -0
  146. package/dist/contract/lock-secret-guard.d.ts.map +1 -0
  147. package/dist/contract/lock-secret-guard.js +134 -0
  148. package/dist/contract/lock-secret-guard.js.map +1 -0
  149. package/dist/contract/lock-vs-git-head.d.ts +26 -0
  150. package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
  151. package/dist/contract/lock-vs-git-head.js +28 -0
  152. package/dist/contract/lock-vs-git-head.js.map +1 -0
  153. package/dist/contract/normalize-contract-step.d.ts +21 -0
  154. package/dist/contract/normalize-contract-step.d.ts.map +1 -0
  155. package/dist/contract/normalize-contract-step.js +70 -0
  156. package/dist/contract/normalize-contract-step.js.map +1 -0
  157. package/dist/contract/normalize-feature-steps.d.ts +7 -0
  158. package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
  159. package/dist/contract/normalize-feature-steps.js +127 -0
  160. package/dist/contract/normalize-feature-steps.js.map +1 -0
  161. package/dist/contract/oracle-strength.d.ts +12 -0
  162. package/dist/contract/oracle-strength.d.ts.map +1 -0
  163. package/dist/contract/oracle-strength.js +88 -0
  164. package/dist/contract/oracle-strength.js.map +1 -0
  165. package/dist/contract/project-contract-blockers.d.ts +10 -0
  166. package/dist/contract/project-contract-blockers.d.ts.map +1 -0
  167. package/dist/contract/project-contract-blockers.js +73 -0
  168. package/dist/contract/project-contract-blockers.js.map +1 -0
  169. package/dist/contract/record-contract-approval.d.ts +21 -0
  170. package/dist/contract/record-contract-approval.d.ts.map +1 -0
  171. package/dist/contract/record-contract-approval.js +98 -0
  172. package/dist/contract/record-contract-approval.js.map +1 -0
  173. package/dist/contract/render-free-gherkin.d.ts +40 -0
  174. package/dist/contract/render-free-gherkin.d.ts.map +1 -0
  175. package/dist/contract/render-free-gherkin.js +141 -0
  176. package/dist/contract/render-free-gherkin.js.map +1 -0
  177. package/dist/contract/render-trace-sentence.d.ts +20 -0
  178. package/dist/contract/render-trace-sentence.d.ts.map +1 -0
  179. package/dist/contract/render-trace-sentence.js +109 -0
  180. package/dist/contract/render-trace-sentence.js.map +1 -0
  181. package/dist/contract/render-trace-target.d.ts +14 -0
  182. package/dist/contract/render-trace-target.d.ts.map +1 -0
  183. package/dist/contract/render-trace-target.js +70 -0
  184. package/dist/contract/render-trace-target.js.map +1 -0
  185. package/dist/contract/resolve-unit-trace-identities.d.ts +8 -0
  186. package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
  187. package/dist/contract/resolve-unit-trace-identities.js +78 -0
  188. package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
  189. package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
  190. package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
  191. package/dist/contract/trace/collect-trace-evidence.js +161 -0
  192. package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
  193. package/dist/contract/trace/identity-cache.d.ts +19 -0
  194. package/dist/contract/trace/identity-cache.d.ts.map +1 -0
  195. package/dist/contract/trace/identity-cache.js +104 -0
  196. package/dist/contract/trace/identity-cache.js.map +1 -0
  197. package/dist/contract/trace/select-final-attempt.d.ts +27 -0
  198. package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
  199. package/dist/contract/trace/select-final-attempt.js +116 -0
  200. package/dist/contract/trace/select-final-attempt.js.map +1 -0
  201. package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
  202. package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
  203. package/dist/contract/trace/snapshot-html-renderer.js +172 -0
  204. package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
  205. package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
  206. package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
  207. package/dist/contract/trace/snapshot-resolver.js +65 -0
  208. package/dist/contract/trace/snapshot-resolver.js.map +1 -0
  209. package/dist/contract/trace/snapshot-store.d.ts +66 -0
  210. package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
  211. package/dist/contract/trace/snapshot-store.js +173 -0
  212. package/dist/contract/trace/snapshot-store.js.map +1 -0
  213. package/dist/contract/trace/target-identity.d.ts +74 -0
  214. package/dist/contract/trace/target-identity.d.ts.map +1 -0
  215. package/dist/contract/trace/target-identity.js +68 -0
  216. package/dist/contract/trace/target-identity.js.map +1 -0
  217. package/dist/contract/trace/test-step-join.d.ts +65 -0
  218. package/dist/contract/trace/test-step-join.d.ts.map +1 -0
  219. package/dist/contract/trace/test-step-join.js +80 -0
  220. package/dist/contract/trace/test-step-join.js.map +1 -0
  221. package/dist/contract/trace/trace-binding.d.ts +21 -0
  222. package/dist/contract/trace/trace-binding.d.ts.map +1 -0
  223. package/dist/contract/trace/trace-binding.js +88 -0
  224. package/dist/contract/trace/trace-binding.js.map +1 -0
  225. package/dist/contract/trace/trace-file-reader.d.ts +35 -0
  226. package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
  227. package/dist/contract/trace/trace-file-reader.js +155 -0
  228. package/dist/contract/trace/trace-file-reader.js.map +1 -0
  229. package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
  230. package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
  231. package/dist/contract/trace/trace-fingerprint.js +96 -0
  232. package/dist/contract/trace/trace-fingerprint.js.map +1 -0
  233. package/dist/contract/trace/trace-identity-resolution.d.ts +26 -0
  234. package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
  235. package/dist/contract/trace/trace-identity-resolution.js +84 -0
  236. package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
  237. package/dist/contract/trace/trace-run-record.d.ts +27 -0
  238. package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
  239. package/dist/contract/trace/trace-run-record.js +115 -0
  240. package/dist/contract/trace/trace-run-record.js.map +1 -0
  241. package/dist/contract/trace/trace-steps.d.ts +19 -0
  242. package/dist/contract/trace/trace-steps.d.ts.map +1 -0
  243. package/dist/contract/trace/trace-steps.js +79 -0
  244. package/dist/contract/trace/trace-steps.js.map +1 -0
  245. package/dist/contract/trace/trace-zip-limits.d.ts +41 -0
  246. package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
  247. package/dist/contract/trace/trace-zip-limits.js +48 -0
  248. package/dist/contract/trace/trace-zip-limits.js.map +1 -0
  249. package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
  250. package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
  251. package/dist/contract/trace/trace-zip-reader.js +186 -0
  252. package/dist/contract/trace/trace-zip-reader.js.map +1 -0
  253. package/dist/contract/verify-approval-decisions.d.ts +5 -0
  254. package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
  255. package/dist/contract/verify-approval-decisions.js +104 -0
  256. package/dist/contract/verify-approval-decisions.js.map +1 -0
  257. package/dist/decisions/append-decision.d.ts +12 -0
  258. package/dist/decisions/append-decision.d.ts.map +1 -0
  259. package/dist/decisions/append-decision.js +73 -0
  260. package/dist/decisions/append-decision.js.map +1 -0
  261. package/dist/decisions/confirmed-decision-commands.d.ts +30 -0
  262. package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
  263. package/dist/decisions/confirmed-decision-commands.js +172 -0
  264. package/dist/decisions/confirmed-decision-commands.js.map +1 -0
  265. package/dist/decisions/decision-file.d.ts +31 -0
  266. package/dist/decisions/decision-file.d.ts.map +1 -0
  267. package/dist/decisions/decision-file.js +139 -0
  268. package/dist/decisions/decision-file.js.map +1 -0
  269. package/dist/decisions/decision-identity.d.ts +12 -0
  270. package/dist/decisions/decision-identity.d.ts.map +1 -0
  271. package/dist/decisions/decision-identity.js +78 -0
  272. package/dist/decisions/decision-identity.js.map +1 -0
  273. package/dist/decisions/decision-integrity-check.d.ts +12 -0
  274. package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
  275. package/dist/decisions/decision-integrity-check.js +102 -0
  276. package/dist/decisions/decision-integrity-check.js.map +1 -0
  277. package/dist/decisions/decision-schema.d.ts +42 -0
  278. package/dist/decisions/decision-schema.d.ts.map +1 -0
  279. package/dist/decisions/decision-schema.js +75 -0
  280. package/dist/decisions/decision-schema.js.map +1 -0
  281. package/dist/decisions/decision-source.d.ts +2 -0
  282. package/dist/decisions/decision-source.d.ts.map +1 -0
  283. package/dist/decisions/decision-source.js +18 -0
  284. package/dist/decisions/decision-source.js.map +1 -0
  285. package/dist/decisions/exclusive-file-create.d.ts +3 -0
  286. package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
  287. package/dist/decisions/exclusive-file-create.js +84 -0
  288. package/dist/decisions/exclusive-file-create.js.map +1 -0
  289. package/dist/decisions/git-head-trust.d.ts +7 -0
  290. package/dist/decisions/git-head-trust.d.ts.map +1 -0
  291. package/dist/decisions/git-head-trust.js +76 -0
  292. package/dist/decisions/git-head-trust.js.map +1 -0
  293. package/dist/decisions/human-confirmation.d.ts +33 -0
  294. package/dist/decisions/human-confirmation.d.ts.map +1 -0
  295. package/dist/decisions/human-confirmation.js +155 -0
  296. package/dist/decisions/human-confirmation.js.map +1 -0
  297. package/dist/decisions/legacy-waiver-migration.d.ts +31 -0
  298. package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
  299. package/dist/decisions/legacy-waiver-migration.js +170 -0
  300. package/dist/decisions/legacy-waiver-migration.js.map +1 -0
  301. package/dist/decisions/read-decisions.d.ts +19 -0
  302. package/dist/decisions/read-decisions.d.ts.map +1 -0
  303. package/dist/decisions/read-decisions.js +90 -0
  304. package/dist/decisions/read-decisions.js.map +1 -0
  305. package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
  306. package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
  307. package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
  308. package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
  309. package/dist/decisions/waiver-decisions.d.ts +24 -0
  310. package/dist/decisions/waiver-decisions.d.ts.map +1 -0
  311. package/dist/decisions/waiver-decisions.js +46 -0
  312. package/dist/decisions/waiver-decisions.js.map +1 -0
  313. package/dist/exporters/csv-exporter.d.ts +4 -0
  314. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  315. package/dist/exporters/csv-exporter.js +1 -1
  316. package/dist/exporters/csv-exporter.js.map +1 -1
  317. package/dist/exporters/feature-parser.d.ts +8 -0
  318. package/dist/exporters/feature-parser.d.ts.map +1 -1
  319. package/dist/exporters/feature-parser.js +12 -0
  320. package/dist/exporters/feature-parser.js.map +1 -1
  321. package/dist/exporters/matrix/build.d.ts +3 -0
  322. package/dist/exporters/matrix/build.d.ts.map +1 -1
  323. package/dist/exporters/matrix/build.js +59 -19
  324. package/dist/exporters/matrix/build.js.map +1 -1
  325. package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
  326. package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
  327. package/dist/exporters/matrix/carry-manual-results.js +76 -6
  328. package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
  329. package/dist/exporters/matrix/export.d.ts +8 -2
  330. package/dist/exporters/matrix/export.d.ts.map +1 -1
  331. package/dist/exporters/matrix/export.js +16 -3
  332. package/dist/exporters/matrix/export.js.map +1 -1
  333. package/dist/exporters/matrix/gates.d.ts +6 -1
  334. package/dist/exporters/matrix/gates.d.ts.map +1 -1
  335. package/dist/exporters/matrix/gates.js +57 -128
  336. package/dist/exporters/matrix/gates.js.map +1 -1
  337. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  338. package/dist/exporters/matrix/render-csv.js +4 -1
  339. package/dist/exporters/matrix/render-csv.js.map +1 -1
  340. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
  341. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
  342. package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
  343. package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
  344. package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
  345. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  346. package/dist/exporters/matrix/render-xlsx.js +6 -0
  347. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  348. package/dist/exporters/matrix/types.d.ts +9 -1
  349. package/dist/exporters/matrix/types.d.ts.map +1 -1
  350. package/dist/exporters/matrix/types.js.map +1 -1
  351. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  352. package/dist/exporters/playwright-report-parser.js +1 -0
  353. package/dist/exporters/playwright-report-parser.js.map +1 -1
  354. package/dist/exporters/scenario-merger.d.ts.map +1 -1
  355. package/dist/exporters/scenario-merger.js +2 -0
  356. package/dist/exporters/scenario-merger.js.map +1 -1
  357. package/dist/exporters/types.d.ts +5 -0
  358. package/dist/exporters/types.d.ts.map +1 -1
  359. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  360. package/dist/generators/test-generator/code-generator.js +13 -0
  361. package/dist/generators/test-generator/code-generator.js.map +1 -1
  362. package/dist/graph/evaluate-graph.d.ts +21 -0
  363. package/dist/graph/evaluate-graph.d.ts.map +1 -0
  364. package/dist/graph/evaluate-graph.js +49 -0
  365. package/dist/graph/evaluate-graph.js.map +1 -0
  366. package/dist/graph/locate-finding.d.ts +20 -0
  367. package/dist/graph/locate-finding.d.ts.map +1 -0
  368. package/dist/graph/locate-finding.js +137 -0
  369. package/dist/graph/locate-finding.js.map +1 -0
  370. package/dist/graph/node-decl.d.ts +59 -0
  371. package/dist/graph/node-decl.d.ts.map +1 -0
  372. package/dist/graph/node-decl.js +8 -0
  373. package/dist/graph/node-decl.js.map +1 -0
  374. package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
  375. package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
  376. package/dist/graph/nodes/core-node-catalog.js +13 -0
  377. package/dist/graph/nodes/core-node-catalog.js.map +1 -0
  378. package/dist/graph/nodes/test-case-contract-nodes.d.ts +19 -0
  379. package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
  380. package/dist/graph/nodes/test-case-contract-nodes.js +56 -0
  381. package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
  382. package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
  383. package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
  384. package/dist/graph/nodes/test-case-nodes.js +66 -0
  385. package/dist/graph/nodes/test-case-nodes.js.map +1 -0
  386. package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
  387. package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
  388. package/dist/graph/nodes/unit-audit-nodes.js +95 -0
  389. package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
  390. package/dist/graph/nodes/unit-contract-node.d.ts +20 -0
  391. package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
  392. package/dist/graph/nodes/unit-contract-node.js +81 -0
  393. package/dist/graph/nodes/unit-contract-node.js.map +1 -0
  394. package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
  395. package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
  396. package/dist/graph/nodes/unit-design-nodes.js +90 -0
  397. package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
  398. package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
  399. package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
  400. package/dist/graph/nodes/unit-execution-nodes.js +79 -0
  401. package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
  402. package/dist/graph/test-case-identity.d.ts +13 -0
  403. package/dist/graph/test-case-identity.d.ts.map +1 -0
  404. package/dist/graph/test-case-identity.js +106 -0
  405. package/dist/graph/test-case-identity.js.map +1 -0
  406. package/dist/graph/test-case-results.d.ts +11 -0
  407. package/dist/graph/test-case-results.d.ts.map +1 -0
  408. package/dist/graph/test-case-results.js +137 -0
  409. package/dist/graph/test-case-results.js.map +1 -0
  410. package/dist/graph/test-case-satisfied.d.ts +25 -0
  411. package/dist/graph/test-case-satisfied.d.ts.map +1 -0
  412. package/dist/graph/test-case-satisfied.js +51 -0
  413. package/dist/graph/test-case-satisfied.js.map +1 -0
  414. package/dist/graph/test-case-state-shape.d.ts +67 -0
  415. package/dist/graph/test-case-state-shape.d.ts.map +1 -0
  416. package/dist/graph/test-case-state-shape.js +3 -0
  417. package/dist/graph/test-case-state-shape.js.map +1 -0
  418. package/dist/graph/test-case-states.d.ts +9 -0
  419. package/dist/graph/test-case-states.d.ts.map +1 -0
  420. package/dist/graph/test-case-states.js +116 -0
  421. package/dist/graph/test-case-states.js.map +1 -0
  422. package/dist/graph/unit-artifact-probes.d.ts +19 -0
  423. package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
  424. package/dist/graph/unit-artifact-probes.js +133 -0
  425. package/dist/graph/unit-artifact-probes.js.map +1 -0
  426. package/dist/graph/unit-state-facts.d.ts +43 -0
  427. package/dist/graph/unit-state-facts.d.ts.map +1 -0
  428. package/dist/graph/unit-state-facts.js +87 -0
  429. package/dist/graph/unit-state-facts.js.map +1 -0
  430. package/dist/harness/audit.d.ts +11 -0
  431. package/dist/harness/audit.d.ts.map +1 -1
  432. package/dist/harness/audit.js +34 -9
  433. package/dist/harness/audit.js.map +1 -1
  434. package/dist/harness/catalog/gate-rules.yaml +647 -0
  435. package/dist/harness/counted-assertions.d.ts +2 -0
  436. package/dist/harness/counted-assertions.d.ts.map +1 -0
  437. package/dist/harness/counted-assertions.js +38 -0
  438. package/dist/harness/counted-assertions.js.map +1 -0
  439. package/dist/harness/defect-draft.d.ts +14 -0
  440. package/dist/harness/defect-draft.d.ts.map +1 -0
  441. package/dist/harness/defect-draft.js +143 -0
  442. package/dist/harness/defect-draft.js.map +1 -0
  443. package/dist/harness/defects.d.ts +24 -0
  444. package/dist/harness/defects.d.ts.map +1 -0
  445. package/dist/harness/defects.js +118 -0
  446. package/dist/harness/defects.js.map +1 -0
  447. package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
  448. package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
  449. package/dist/harness/gates/audit-gate-terms.js +72 -0
  450. package/dist/harness/gates/audit-gate-terms.js.map +1 -0
  451. package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
  452. package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
  453. package/dist/harness/gates/audit-gate-verdict.js +101 -0
  454. package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
  455. package/dist/harness/gates/audit-input-hash.d.ts +4 -0
  456. package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
  457. package/dist/harness/gates/audit-input-hash.js +96 -0
  458. package/dist/harness/gates/audit-input-hash.js.map +1 -0
  459. package/dist/harness/gates/audit-report-verification.d.ts +32 -0
  460. package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
  461. package/dist/harness/gates/audit-report-verification.js +207 -0
  462. package/dist/harness/gates/audit-report-verification.js.map +1 -0
  463. package/dist/harness/gates/decisions-hash.d.ts +13 -0
  464. package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
  465. package/dist/harness/gates/decisions-hash.js +72 -0
  466. package/dist/harness/gates/decisions-hash.js.map +1 -0
  467. package/dist/harness/gates/delivery-checklist.d.ts +21 -0
  468. package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
  469. package/dist/harness/gates/delivery-checklist.js +128 -0
  470. package/dist/harness/gates/delivery-checklist.js.map +1 -0
  471. package/dist/harness/gates/delivery-decision-gate.d.ts +29 -0
  472. package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
  473. package/dist/harness/gates/delivery-decision-gate.js +91 -0
  474. package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
  475. package/dist/harness/gates/delivery-decision-step.d.ts +16 -0
  476. package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
  477. package/dist/harness/gates/delivery-decision-step.js +71 -0
  478. package/dist/harness/gates/delivery-decision-step.js.map +1 -0
  479. package/dist/harness/gates/gate-override-report.d.ts +59 -0
  480. package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
  481. package/dist/harness/gates/gate-override-report.js +86 -0
  482. package/dist/harness/gates/gate-override-report.js.map +1 -0
  483. package/dist/harness/gates/gate-override-scope.d.ts +18 -0
  484. package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
  485. package/dist/harness/gates/gate-override-scope.js +98 -0
  486. package/dist/harness/gates/gate-override-scope.js.map +1 -0
  487. package/dist/harness/gates/gate-override-validation.d.ts +25 -0
  488. package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
  489. package/dist/harness/gates/gate-override-validation.js +81 -0
  490. package/dist/harness/gates/gate-override-validation.js.map +1 -0
  491. package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
  492. package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
  493. package/dist/harness/gates/gate-rule-catalog.js +112 -0
  494. package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
  495. package/dist/harness/gates/gate-terms.d.ts +33 -0
  496. package/dist/harness/gates/gate-terms.d.ts.map +1 -0
  497. package/dist/harness/gates/gate-terms.js +41 -0
  498. package/dist/harness/gates/gate-terms.js.map +1 -0
  499. package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
  500. package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
  501. package/dist/harness/gates/in-run-decision-check.js +138 -0
  502. package/dist/harness/gates/in-run-decision-check.js.map +1 -0
  503. package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
  504. package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
  505. package/dist/harness/gates/journey-gate-inputs.js +81 -0
  506. package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
  507. package/dist/harness/gates/migrated-waiver-review.d.ts +6 -0
  508. package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
  509. package/dist/harness/gates/migrated-waiver-review.js +116 -0
  510. package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
  511. package/dist/harness/gates/resolve-rule.d.ts +59 -0
  512. package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
  513. package/dist/harness/gates/resolve-rule.js +41 -0
  514. package/dist/harness/gates/resolve-rule.js.map +1 -0
  515. package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
  516. package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
  517. package/dist/harness/gates/unit-gate-overrides.js +122 -0
  518. package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
  519. package/dist/harness/journey.d.ts +24 -1
  520. package/dist/harness/journey.d.ts.map +1 -1
  521. package/dist/harness/journey.js +114 -23
  522. package/dist/harness/journey.js.map +1 -1
  523. package/dist/harness/manifest.d.ts +12 -0
  524. package/dist/harness/manifest.d.ts.map +1 -1
  525. package/dist/harness/manifest.js +73 -45
  526. package/dist/harness/manifest.js.map +1 -1
  527. package/dist/harness/next-step.d.ts +37 -6
  528. package/dist/harness/next-step.d.ts.map +1 -1
  529. package/dist/harness/next-step.js +34 -174
  530. package/dist/harness/next-step.js.map +1 -1
  531. package/dist/harness/parse.d.ts +25 -0
  532. package/dist/harness/parse.d.ts.map +1 -1
  533. package/dist/harness/parse.js +38 -13
  534. package/dist/harness/parse.js.map +1 -1
  535. package/dist/harness/script-check.d.ts.map +1 -1
  536. package/dist/harness/script-check.js +29 -6
  537. package/dist/harness/script-check.js.map +1 -1
  538. package/dist/harness/sensors.d.ts +14 -3
  539. package/dist/harness/sensors.d.ts.map +1 -1
  540. package/dist/harness/sensors.js +64 -44
  541. package/dist/harness/sensors.js.map +1 -1
  542. package/dist/harness/triage-signals.d.ts +20 -0
  543. package/dist/harness/triage-signals.d.ts.map +1 -0
  544. package/dist/harness/triage-signals.js +30 -0
  545. package/dist/harness/triage-signals.js.map +1 -0
  546. package/dist/harness/triage-unit.d.ts +23 -0
  547. package/dist/harness/triage-unit.d.ts.map +1 -0
  548. package/dist/harness/triage-unit.js +142 -0
  549. package/dist/harness/triage-unit.js.map +1 -0
  550. package/dist/harness/triage.d.ts +28 -0
  551. package/dist/harness/triage.d.ts.map +1 -0
  552. package/dist/harness/triage.js +99 -0
  553. package/dist/harness/triage.js.map +1 -0
  554. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  555. package/dist/harness/viewpoint-ledger.js +2 -1
  556. package/dist/harness/viewpoint-ledger.js.map +1 -1
  557. package/dist/hooks/bash-write-targets.d.ts +15 -0
  558. package/dist/hooks/bash-write-targets.d.ts.map +1 -0
  559. package/dist/hooks/bash-write-targets.js +173 -0
  560. package/dist/hooks/bash-write-targets.js.map +1 -0
  561. package/dist/hooks/claude-hook-entry.d.ts +11 -0
  562. package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
  563. package/dist/hooks/claude-hook-entry.js +137 -0
  564. package/dist/hooks/claude-hook-entry.js.map +1 -0
  565. package/dist/hooks/claude-transcript-turn.d.ts +11 -0
  566. package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
  567. package/dist/hooks/claude-transcript-turn.js +143 -0
  568. package/dist/hooks/claude-transcript-turn.js.map +1 -0
  569. package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
  570. package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
  571. package/dist/hooks/guard-delivery-contract-rule.js +152 -0
  572. package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
  573. package/dist/hooks/guard-protected-paths.d.ts +9 -0
  574. package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
  575. package/dist/hooks/guard-protected-paths.js +106 -0
  576. package/dist/hooks/guard-protected-paths.js.map +1 -0
  577. package/dist/hooks/guard-rules.d.ts +37 -0
  578. package/dist/hooks/guard-rules.d.ts.map +1 -0
  579. package/dist/hooks/guard-rules.js +61 -0
  580. package/dist/hooks/guard-rules.js.map +1 -0
  581. package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
  582. package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
  583. package/dist/hooks/guard-tty-spoof-rule.js +21 -0
  584. package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
  585. package/dist/hooks/hand-back-core.d.ts +37 -0
  586. package/dist/hooks/hand-back-core.d.ts.map +1 -0
  587. package/dist/hooks/hand-back-core.js +115 -0
  588. package/dist/hooks/hand-back-core.js.map +1 -0
  589. package/dist/hooks/hook-cli-command.d.ts +9 -0
  590. package/dist/hooks/hook-cli-command.d.ts.map +1 -0
  591. package/dist/hooks/hook-cli-command.js +46 -0
  592. package/dist/hooks/hook-cli-command.js.map +1 -0
  593. package/dist/hooks/hook-registry.d.ts +10 -0
  594. package/dist/hooks/hook-registry.d.ts.map +1 -0
  595. package/dist/hooks/hook-registry.js +24 -0
  596. package/dist/hooks/hook-registry.js.map +1 -0
  597. package/dist/hooks/hook-result-contract.d.ts +50 -0
  598. package/dist/hooks/hook-result-contract.d.ts.map +1 -0
  599. package/dist/hooks/hook-result-contract.js +80 -0
  600. package/dist/hooks/hook-result-contract.js.map +1 -0
  601. package/dist/hooks/hook-toggles.d.ts +10 -0
  602. package/dist/hooks/hook-toggles.d.ts.map +1 -0
  603. package/dist/hooks/hook-toggles.js +107 -0
  604. package/dist/hooks/hook-toggles.js.map +1 -0
  605. package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
  606. package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
  607. package/dist/hooks/pre-tool-guard-hook.js +105 -0
  608. package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
  609. package/dist/hooks/session-start-unit-state-hook.d.ts +13 -0
  610. package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
  611. package/dist/hooks/session-start-unit-state-hook.js +55 -0
  612. package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
  613. package/dist/hooks/stop-next-step-hook.d.ts +16 -0
  614. package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
  615. package/dist/hooks/stop-next-step-hook.js +42 -0
  616. package/dist/hooks/stop-next-step-hook.js.map +1 -0
  617. package/dist/hooks/sungen-project-root.d.ts +19 -0
  618. package/dist/hooks/sungen-project-root.d.ts.map +1 -0
  619. package/dist/hooks/sungen-project-root.js +130 -0
  620. package/dist/hooks/sungen-project-root.js.map +1 -0
  621. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
  622. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
  623. package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
  624. package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
  625. package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
  626. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
  627. package/dist/orchestrator/ai-skills/commands.js +16 -6
  628. package/dist/orchestrator/ai-skills/commands.js.map +1 -1
  629. package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
  630. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -1
  631. package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
  632. package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
  633. package/dist/orchestrator/claude-hooks-install.js +203 -0
  634. package/dist/orchestrator/claude-hooks-install.js.map +1 -0
  635. package/dist/orchestrator/lockstep-version.d.ts +15 -5
  636. package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
  637. package/dist/orchestrator/lockstep-version.js +30 -17
  638. package/dist/orchestrator/lockstep-version.js.map +1 -1
  639. package/dist/orchestrator/project-initializer.d.ts +6 -0
  640. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  641. package/dist/orchestrator/project-initializer.js +23 -0
  642. package/dist/orchestrator/project-initializer.js.map +1 -1
  643. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  644. package/dist/orchestrator/screen-manager.js +4 -2
  645. package/dist/orchestrator/screen-manager.js.map +1 -1
  646. package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  647. package/dist/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  648. package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  649. package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  650. package/dist/orchestrator/templates/ai-src/commands/create-test.md +14 -16
  651. package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  652. package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  653. package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
  654. package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  655. package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
  656. package/dist/orchestrator/templates/ai-src/commands/review.md +2 -0
  657. package/dist/orchestrator/templates/ai-src/commands/run-test.md +14 -10
  658. package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
  659. package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
  660. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  661. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  662. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  663. package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  664. package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  665. package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  666. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
  667. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
  668. package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
  669. package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  670. package/dist/utils/safe-yaml-parse.d.ts +8 -0
  671. package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
  672. package/dist/utils/safe-yaml-parse.js +51 -0
  673. package/dist/utils/safe-yaml-parse.js.map +1 -0
  674. package/package.json +3 -3
  675. package/src/basis/anchor-grammar.ts +83 -0
  676. package/src/basis/anchor-hash.ts +27 -0
  677. package/src/basis/bm51-tables.ts +65 -0
  678. package/src/basis/extract-spec-anchors.ts +95 -0
  679. package/src/basis/migration-sentinel.ts +175 -0
  680. package/src/basis/spec-frontmatter.ts +68 -0
  681. package/src/basis/spec-sections.ts +35 -0
  682. package/src/basis/test-basis-schema.ts +81 -0
  683. package/src/capabilities/registry.ts +16 -0
  684. package/src/cli/commands/audit.ts +9 -4
  685. package/src/cli/commands/capability.ts +4 -2
  686. package/src/cli/commands/contract-derive-command.ts +60 -0
  687. package/src/cli/commands/contract.ts +130 -0
  688. package/src/cli/commands/defect.ts +49 -0
  689. package/src/cli/commands/delivery.ts +85 -13
  690. package/src/cli/commands/generate.ts +3 -1
  691. package/src/cli/commands/journey.ts +39 -14
  692. package/src/cli/commands/next.ts +60 -8
  693. package/src/cli/commands/triage.ts +57 -0
  694. package/src/cli/commands/update.ts +35 -8
  695. package/src/cli/index.ts +24 -30
  696. package/src/contract/approve-contract.ts +94 -0
  697. package/src/contract/assertion-matcher-vocabulary.ts +93 -0
  698. package/src/contract/build-contract-verdict.ts +108 -0
  699. package/src/contract/build-trace-conformance.ts +100 -0
  700. package/src/contract/classify-contract-change.ts +92 -0
  701. package/src/contract/classify-contract-unit.ts +47 -0
  702. package/src/contract/classify-trace-conformance.ts +116 -0
  703. package/src/contract/contract-git.ts +106 -0
  704. package/src/contract/contract-hash.ts +34 -0
  705. package/src/contract/contract-report-file.ts +25 -0
  706. package/src/contract/contract-schema.ts +137 -0
  707. package/src/contract/contract-units.ts +52 -0
  708. package/src/contract/contracts-lock-file.ts +97 -0
  709. package/src/contract/data-normalize.ts +66 -0
  710. package/src/contract/derive-free-feature.ts +110 -0
  711. package/src/contract/field-semantic-redaction.ts +80 -0
  712. package/src/contract/free-feature-blocks.ts +56 -0
  713. package/src/contract/lock-secret-guard.ts +95 -0
  714. package/src/contract/lock-vs-git-head.ts +38 -0
  715. package/src/contract/normalize-contract-step.ts +74 -0
  716. package/src/contract/normalize-feature-steps.ts +93 -0
  717. package/src/contract/oracle-strength.ts +80 -0
  718. package/src/contract/project-contract-blockers.ts +36 -0
  719. package/src/contract/record-contract-approval.ts +70 -0
  720. package/src/contract/render-free-gherkin.ts +113 -0
  721. package/src/contract/render-trace-sentence.ts +98 -0
  722. package/src/contract/render-trace-target.ts +68 -0
  723. package/src/contract/resolve-unit-trace-identities.ts +35 -0
  724. package/src/contract/trace/collect-trace-evidence.ts +109 -0
  725. package/src/contract/trace/identity-cache.ts +61 -0
  726. package/src/contract/trace/select-final-attempt.ts +70 -0
  727. package/src/contract/trace/snapshot-html-renderer.ts +136 -0
  728. package/src/contract/trace/snapshot-resolver.ts +66 -0
  729. package/src/contract/trace/snapshot-store.ts +151 -0
  730. package/src/contract/trace/target-identity.ts +104 -0
  731. package/src/contract/trace/test-step-join.ts +103 -0
  732. package/src/contract/trace/trace-binding.ts +56 -0
  733. package/src/contract/trace/trace-file-reader.ts +123 -0
  734. package/src/contract/trace/trace-fingerprint.ts +68 -0
  735. package/src/contract/trace/trace-identity-resolution.ts +74 -0
  736. package/src/contract/trace/trace-run-record.ts +74 -0
  737. package/src/contract/trace/trace-steps.ts +88 -0
  738. package/src/contract/trace/trace-zip-limits.ts +73 -0
  739. package/src/contract/trace/trace-zip-reader.ts +127 -0
  740. package/src/contract/verify-approval-decisions.ts +60 -0
  741. package/src/decisions/append-decision.ts +35 -0
  742. package/src/decisions/confirmed-decision-commands.ts +128 -0
  743. package/src/decisions/decision-file.ts +93 -0
  744. package/src/decisions/decision-identity.ts +73 -0
  745. package/src/decisions/decision-integrity-check.ts +61 -0
  746. package/src/decisions/decision-schema.ts +76 -0
  747. package/src/decisions/decision-source.ts +14 -0
  748. package/src/decisions/exclusive-file-create.ts +42 -0
  749. package/src/decisions/git-head-trust.ts +32 -0
  750. package/src/decisions/human-confirmation.ts +114 -0
  751. package/src/decisions/legacy-waiver-migration.ts +135 -0
  752. package/src/decisions/read-decisions.ts +61 -0
  753. package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
  754. package/src/decisions/waiver-decisions.ts +56 -0
  755. package/src/exporters/csv-exporter.ts +5 -1
  756. package/src/exporters/feature-parser.ts +12 -0
  757. package/src/exporters/matrix/build.ts +62 -18
  758. package/src/exporters/matrix/carry-manual-results.ts +70 -7
  759. package/src/exporters/matrix/export.ts +17 -2
  760. package/src/exporters/matrix/gates.ts +62 -129
  761. package/src/exporters/matrix/render-csv.ts +4 -1
  762. package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
  763. package/src/exporters/matrix/render-xlsx.ts +6 -0
  764. package/src/exporters/matrix/types.ts +9 -1
  765. package/src/exporters/playwright-report-parser.ts +1 -0
  766. package/src/exporters/scenario-merger.ts +2 -1
  767. package/src/exporters/types.ts +5 -0
  768. package/src/generators/test-generator/code-generator.ts +13 -0
  769. package/src/graph/evaluate-graph.ts +62 -0
  770. package/src/graph/locate-finding.ts +93 -0
  771. package/src/graph/node-decl.ts +65 -0
  772. package/src/graph/nodes/core-node-catalog.ts +15 -0
  773. package/src/graph/nodes/test-case-contract-nodes.ts +69 -0
  774. package/src/graph/nodes/test-case-nodes.ts +68 -0
  775. package/src/graph/nodes/unit-audit-nodes.ts +95 -0
  776. package/src/graph/nodes/unit-contract-node.ts +82 -0
  777. package/src/graph/nodes/unit-design-nodes.ts +88 -0
  778. package/src/graph/nodes/unit-execution-nodes.ts +77 -0
  779. package/src/graph/test-case-identity.ts +75 -0
  780. package/src/graph/test-case-results.ts +90 -0
  781. package/src/graph/test-case-satisfied.ts +55 -0
  782. package/src/graph/test-case-state-shape.ts +62 -0
  783. package/src/graph/test-case-states.ts +84 -0
  784. package/src/graph/unit-artifact-probes.ts +77 -0
  785. package/src/graph/unit-state-facts.ts +112 -0
  786. package/src/harness/audit.ts +46 -11
  787. package/src/harness/catalog/gate-rules.yaml +647 -0
  788. package/src/harness/counted-assertions.ts +33 -0
  789. package/src/harness/defect-draft.ts +115 -0
  790. package/src/harness/defects.ts +94 -0
  791. package/src/harness/gates/audit-gate-terms.ts +80 -0
  792. package/src/harness/gates/audit-gate-verdict.ts +78 -0
  793. package/src/harness/gates/audit-input-hash.ts +55 -0
  794. package/src/harness/gates/audit-report-verification.ts +173 -0
  795. package/src/harness/gates/decisions-hash.ts +42 -0
  796. package/src/harness/gates/delivery-checklist.ts +120 -0
  797. package/src/harness/gates/delivery-decision-gate.ts +111 -0
  798. package/src/harness/gates/delivery-decision-step.ts +73 -0
  799. package/src/harness/gates/gate-override-report.ts +114 -0
  800. package/src/harness/gates/gate-override-scope.ts +66 -0
  801. package/src/harness/gates/gate-override-validation.ts +75 -0
  802. package/src/harness/gates/gate-rule-catalog.ts +82 -0
  803. package/src/harness/gates/gate-terms.ts +59 -0
  804. package/src/harness/gates/in-run-decision-check.ts +99 -0
  805. package/src/harness/gates/journey-gate-inputs.ts +89 -0
  806. package/src/harness/gates/migrated-waiver-review.ts +68 -0
  807. package/src/harness/gates/resolve-rule.ts +94 -0
  808. package/src/harness/gates/unit-gate-overrides.ts +99 -0
  809. package/src/harness/journey.ts +122 -27
  810. package/src/harness/manifest.ts +69 -45
  811. package/src/harness/next-step.ts +60 -170
  812. package/src/harness/parse.ts +40 -13
  813. package/src/harness/script-check.ts +27 -6
  814. package/src/harness/sensors.ts +73 -47
  815. package/src/harness/triage-signals.ts +46 -0
  816. package/src/harness/triage-unit.ts +113 -0
  817. package/src/harness/triage.ts +127 -0
  818. package/src/harness/viewpoint-ledger.ts +2 -2
  819. package/src/hooks/bash-write-targets.ts +109 -0
  820. package/src/hooks/claude-hook-entry.ts +85 -0
  821. package/src/hooks/claude-transcript-turn.ts +96 -0
  822. package/src/hooks/guard-delivery-contract-rule.ts +110 -0
  823. package/src/hooks/guard-protected-paths.ts +70 -0
  824. package/src/hooks/guard-rules.ts +98 -0
  825. package/src/hooks/guard-tty-spoof-rule.ts +31 -0
  826. package/src/hooks/hand-back-core.ts +106 -0
  827. package/src/hooks/hook-cli-command.ts +17 -0
  828. package/src/hooks/hook-registry.ts +24 -0
  829. package/src/hooks/hook-result-contract.ts +114 -0
  830. package/src/hooks/hook-toggles.ts +66 -0
  831. package/src/hooks/pre-tool-guard-hook.ts +67 -0
  832. package/src/hooks/session-start-unit-state-hook.ts +54 -0
  833. package/src/hooks/stop-next-step-hook.ts +37 -0
  834. package/src/hooks/sungen-project-root.ts +80 -0
  835. package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
  836. package/src/orchestrator/ai-skills/commands.ts +22 -8
  837. package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
  838. package/src/orchestrator/claude-hooks-install.ts +168 -0
  839. package/src/orchestrator/lockstep-version.ts +28 -15
  840. package/src/orchestrator/project-initializer.ts +21 -0
  841. package/src/orchestrator/screen-manager.ts +3 -1
  842. package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  843. package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  844. package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  845. package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  846. package/src/orchestrator/templates/ai-src/commands/create-test.md +14 -16
  847. package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  848. package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  849. package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
  850. package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  851. package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
  852. package/src/orchestrator/templates/ai-src/commands/review.md +2 -0
  853. package/src/orchestrator/templates/ai-src/commands/run-test.md +14 -10
  854. package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
  855. package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
  856. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  857. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  858. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  859. package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  860. package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  861. package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  862. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
  863. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
  864. package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
  865. package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  866. package/src/utils/safe-yaml-parse.ts +44 -0
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The journey CLI's human-gated actions (DOS-13.4), with the terminal passed in so the exact code the
3
+ * CLI runs on process.stdin/stderr is the code a test drives with a simulated TTY.
4
+ *
5
+ * waive `sungen journey --screen <u> --waive <OB> --reason …` phrase: the obligation id
6
+ * decide `sungen journey [--screen <u>] --decide <kind> --text … --reason … [--anchor …]` phrase: the kind
7
+ * migration a leftover write-ahead sentinel → REVERT or KEEP (takumi T6)
8
+ *
9
+ * Everything that can be validated is validated BEFORE the prompt, so a human never confirms
10
+ * something that is then rejected — for a gate-override that includes the override rules (OES-11.3:
11
+ * soft rule, warning|info|off, `off` only where allowed, one unit or a glob in one kind, reason ≥ 15).
12
+ * `humanAccepted: true` is added to a gate-override's payload only AFTER the typed confirmation
13
+ * (takumi T5: present means effective) and can never be supplied through --payload.
14
+ * Without a TTY each refuses and prints the exact command.
15
+ */
16
+ import * as path from 'path';
17
+ import { assertNoPendingMigration, pendingMigrationState, PendingMigration, readPendingMigration, resolvePendingMigration, sentinelPath } from '../basis/migration-sentinel';
18
+ import { parseAnchorId } from '../basis/anchor-grammar';
19
+ import { JourneyReport, runJourney, waive } from '../harness/journey';
20
+ import { reportSlug } from '../harness/unit-paths';
21
+ import { appendDecision, AppendResult } from './append-decision';
22
+ import { PROJECT_SCOPE, unitDecisionScope } from './decision-file';
23
+ import { DecisionDraft, DecisionKind, draftErrors, JsonValue } from './decision-schema';
24
+ import { gitUserName } from './decision-source';
25
+ import { askMigrationChoice, ConfirmationIO, formatCommand, requireTypedConfirmation } from './human-confirmation';
26
+ import { readDecisions } from './read-decisions';
27
+ import { overridePayloadErrors } from '../harness/gates/gate-override-validation';
28
+
29
+ export interface WaiveInput { projectRoot: string; unit: string; obligation: string; reason: string; io: ConfirmationIO }
30
+
31
+ export async function waiveWithConfirmation(i: WaiveInput): Promise<JourneyReport> {
32
+ if (!i.reason || !i.reason.trim()) throw new Error('A reason is required to waive (anti-amnesia: a waiver must record WHY). Use --reason "...".');
33
+ const valid = runJourney(i.projectRoot, i.unit).obligations.map((o) => o.id);
34
+ if (!valid.includes(i.obligation)) throw new Error(`Unknown obligation "${i.obligation}". Valid: ${valid.join(', ')}`);
35
+ await requireTypedConfirmation({
36
+ what: `waive ${i.obligation} for ${i.unit}`,
37
+ phrase: i.obligation,
38
+ command: formatCommand(['sungen', 'journey', '--screen', i.unit, '--waive', i.obligation, '--reason', i.reason]),
39
+ summary: [`reason: ${i.reason.trim()}`, `recorded by: ${gitUserName(i.projectRoot)}`, 'it stops counting as soon as the audit changes (re-decide then)'],
40
+ io: i.io,
41
+ });
42
+ return waive(i.projectRoot, i.unit, i.obligation, i.reason);
43
+ }
44
+
45
+ /** Kinds `--decide` records. The others have a command of their own that computes their payload. */
46
+ export const DECIDE_KINDS: readonly DecisionKind[] = ['gate-override', 'clarification'];
47
+ const OWN_COMMAND: Partial<Record<DecisionKind, string>> = {
48
+ waiver: 'use `sungen journey --screen <unit> --waive <OB-id> --reason "..."`',
49
+ 'contract-approval': 'it is recorded by `sungen contract approve`',
50
+ 'viewpoint-acceptance': 'it is recorded by `sungen audit --accept-viewpoint`',
51
+ };
52
+
53
+ export interface DecideInput {
54
+ projectRoot: string; unit?: string; kind: string; text?: string; reason?: string; anchors?: string[];
55
+ payload?: string; visibility?: string; supersedes?: string; io: ConfirmationIO;
56
+ }
57
+
58
+ function decideDraft(i: DecideInput): { scope: string; draft: DecisionDraft } {
59
+ const kind = i.kind as DecisionKind;
60
+ if (!DECIDE_KINDS.includes(kind)) {
61
+ throw new Error(`--decide ${i.kind}: ${OWN_COMMAND[kind] ?? `kind must be one of ${DECIDE_KINDS.join(', ')}`}.`);
62
+ }
63
+ const errors: string[] = [];
64
+ const anchors = i.anchors ?? [];
65
+ for (const a of anchors) if (!parseAnchorId(a)) errors.push(`--anchor "${a}" is not a spec anchor (SPEC-ID#A-2, SPEC-ID#A-2/val or sec-<slug>)`);
66
+ let payload: Record<string, JsonValue> | undefined;
67
+ if (i.payload !== undefined) {
68
+ try {
69
+ const p = JSON.parse(i.payload);
70
+ if (p && typeof p === 'object' && !Array.isArray(p)) payload = p; else errors.push('--payload must be a JSON object');
71
+ } catch { errors.push('--payload is not valid JSON'); }
72
+ }
73
+ const scope = i.unit ? unitDecisionScope(i.projectRoot, i.unit) : PROJECT_SCOPE;
74
+ if (!scope) errors.push(`--screen ${i.unit} does not name exactly one unit under qa/screens, qa/flows or qa/api`);
75
+ if (kind === 'gate-override') {
76
+ if (payload && 'humanAccepted' in payload) errors.push('--payload must not carry humanAccepted — the terminal confirmation sets it');
77
+ else if (!payload) errors.push('--payload is required for a gate-override: {"rule","severity","scope"}');
78
+ else if (scope) errors.push(...overridePayloadErrors(payload, i.reason ?? '', scope).map((e) => `gate-override: ${e}`));
79
+ }
80
+ if (scope && i.supersedes && !readDecisions(i.projectRoot, scope).entries.some((e) => e.id === i.supersedes)) {
81
+ errors.push(`--supersedes ${i.supersedes} names no decision in ${scope === PROJECT_SCOPE ? 'qa/decisions' : `qa/${scope}/decisions`}`);
82
+ }
83
+ const draft = {
84
+ kind, anchors, text: i.text ?? '', reason: i.reason ?? '', ...(payload ? { payload } : {}),
85
+ source: { by: gitUserName(i.projectRoot), where: 'cli' }, valid_from: new Date().toISOString(),
86
+ ...(i.supersedes ? { supersedes: i.supersedes } : {}), visibility: (i.visibility ?? 'internal') as DecisionDraft['visibility'],
87
+ };
88
+ errors.push(...draftErrors(draft as unknown as Record<string, unknown>).map((e) => e.replace(/^text /, '--text ').replace(/^reason /, '--reason ')));
89
+ if (errors.length) throw new Error(`Cannot record the decision:\n - ${errors.join('\n - ')}`);
90
+ return { scope, draft };
91
+ }
92
+
93
+ export async function decideWithConfirmation(i: DecideInput): Promise<AppendResult> {
94
+ const { scope, draft } = decideDraft(i);
95
+ const argv = ['sungen', 'journey', ...(i.unit ? ['--screen', i.unit] : []), '--decide', draft.kind, '--text', draft.text, '--reason', draft.reason,
96
+ ...draft.anchors.flatMap((a) => ['--anchor', a]), ...(i.payload !== undefined ? ['--payload', i.payload] : []),
97
+ ...(draft.visibility !== 'internal' ? ['--visibility', draft.visibility] : []), ...(draft.supersedes ? ['--supersedes', draft.supersedes] : [])];
98
+ await requireTypedConfirmation({
99
+ what: `record a ${draft.kind} decision in ${scope === PROJECT_SCOPE ? 'qa/decisions' : `qa/${scope}/decisions`}`,
100
+ phrase: draft.kind,
101
+ command: formatCommand(argv),
102
+ summary: [`text: ${draft.text}`, `reason: ${draft.reason}`, ...(draft.anchors.length ? [`anchors: ${draft.anchors.join(', ')}`] : []),
103
+ ...(draft.payload ? [`payload: ${JSON.stringify(draft.payload)}`] : []), `visibility: ${draft.visibility}`, `recorded by: ${draft.source.by}`],
104
+ io: i.io,
105
+ });
106
+ // Present means effective (T5): only now, after a human typed the phrase, is the override accepted.
107
+ const confirmed = draft.kind === 'gate-override' ? { ...draft, payload: { ...draft.payload, humanAccepted: true } } : draft;
108
+ return appendDecision(i.projectRoot, scope, confirmed);
109
+ }
110
+
111
+ function assertNotInProgress(projectRoot: string, slug: string, pending: PendingMigration & { readable: boolean }): void {
112
+ if (pendingMigrationState(pending) === 'in-progress') assertNoPendingMigration(projectRoot, slug); // throws MigrationInProgressError
113
+ }
114
+
115
+ /** A sentinel left by a killed migration: ask REVERT/KEEP on a TTY, refuse without one. null = none pending. */
116
+ export async function answerPendingMigration(projectRoot: string, unit: string, io: ConfirmationIO): Promise<string[] | null> {
117
+ const slug = reportSlug(unit);
118
+ const pending = readPendingMigration(projectRoot, slug);
119
+ if (!pending) return null;
120
+ assertNotInProgress(projectRoot, slug, pending); // another run migrating now: "retry", never a prompt
121
+ const files = pending.files.map((f) => ` ${f.existed ? 'restore' : 'remove '} ${f.path}`).join('\n');
122
+ const choice = await askMigrationChoice({
123
+ detail: `An interrupted migration left ${path.relative(projectRoot, sentinelPath(projectRoot, slug))} (started ${pending.startedAt}: ${pending.reason}).\n${files}`,
124
+ command: formatCommand(['sungen', 'journey', '--screen', unit]),
125
+ io,
126
+ });
127
+ return resolvePendingMigration(projectRoot, slug, choice);
128
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Where decisions live and how one file reads and writes (DOS-13.1, DOS-3.9).
3
+ *
4
+ * project scope qa/decisions/<id>.yaml
5
+ * unit scope qa/<kind>/<unit>/decisions/<id>.yaml scope = `<kind>/<unit>` (e.g. `screens/login`)
6
+ *
7
+ * The scope is not stored in the entry: it is where the file lives, and it is part of the id, so a
8
+ * file moved to another unit no longer matches its own id.
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { stringify } from 'yaml';
13
+ import { resolveUnitCandidates } from '../cli/resolve-unit';
14
+ import { safeYamlParse } from '../utils/safe-yaml-parse';
15
+ import { DecisionEntry, storedEntryErrors } from './decision-schema';
16
+ import { computeDecisionId, computeEntryHash } from './decision-identity';
17
+
18
+ export const PROJECT_SCOPE = 'project';
19
+ /** A decision file's path (`/`-separated, relative to the project): `…/decisions/<name>.yaml`. One test for every reader. */
20
+ export const DECISION_FILE_PATH = /(^|\/)decisions\/[^/]+\.yaml$/;
21
+ const UNIT_SCOPE_RE = /^(?:(?:screens|flows|api)\/(?!\.\.?$)[^/\\]+|api\/flows\/(?!\.\.?$)[^/\\]+)$/;
22
+
23
+ export function isValidScope(scope: string): boolean {
24
+ return scope === PROJECT_SCOPE || UNIT_SCOPE_RE.test(scope);
25
+ }
26
+
27
+ export function decisionsDirFor(projectRoot: string, scope: string): string {
28
+ if (!isValidScope(scope)) throw new Error(`invalid decision scope "${scope}" (project, or <screens|flows|api>/<unit>)`);
29
+ return scope === PROJECT_SCOPE
30
+ ? path.join(projectRoot, 'qa', 'decisions')
31
+ : path.join(projectRoot, 'qa', ...scope.split('/'), 'decisions');
32
+ }
33
+
34
+ /** The unit scope for a unit name, or null when the name matches no unit or more than one. */
35
+ export function unitDecisionScope(projectRoot: string, unit: string): string | null {
36
+ const found = resolveUnitCandidates(unit, projectRoot);
37
+ if (found.length !== 1) return null;
38
+ const scope = path.relative(path.join(projectRoot, 'qa'), found[0].dir).split(path.sep).join('/');
39
+ return isValidScope(scope) ? scope : null;
40
+ }
41
+
42
+ /** Every decisions directory that exists, with its scope. */
43
+ export function listDecisionDirs(projectRoot: string): { scope: string; dir: string }[] {
44
+ const qa = path.join(projectRoot, 'qa');
45
+ const out: { scope: string; dir: string }[] = [];
46
+ const add = (scope: string): void => {
47
+ const dir = decisionsDirFor(projectRoot, scope);
48
+ if (fs.existsSync(dir)) out.push({ scope, dir });
49
+ };
50
+ add(PROJECT_SCOPE);
51
+ const units = (rel: string): string[] => {
52
+ try {
53
+ return fs.readdirSync(path.join(qa, rel), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
54
+ } catch { return []; }
55
+ };
56
+ for (const kind of ['screens', 'flows', 'api']) {
57
+ for (const u of units(kind)) if (!(kind === 'api' && u === 'flows')) add(`${kind}/${u}`);
58
+ }
59
+ for (const u of units('api/flows')) add(`api/flows/${u}`);
60
+ return out;
61
+ }
62
+
63
+ export function serializeDecision(entry: DecisionEntry): string {
64
+ const head = '# Sungen decision — append-only. To change it, record a new entry with `supersedes`.\n';
65
+ return head + stringify(entry, { schema: 'core', lineWidth: 0 });
66
+ }
67
+
68
+ /** `untrusted`: legacy approval state that differs from git HEAD (viewpoint-baseline adapter). */
69
+ export type DecisionProblemCode = 'unreadable' | 'invalid' | 'id-mismatch' | 'hash-mismatch' | 'untrusted';
70
+ export interface DecisionProblem { path: string; code: DecisionProblemCode; message: string }
71
+
72
+ /**
73
+ * Read one decision file and verify it against itself: schema, file name = id, id = content
74
+ * (else `id-mismatch` → decision.id-reuse), entry_hash = entry (else `hash-mismatch` → decision.mutated).
75
+ * Never throws — a bad file is a problem, and the caller decides what that means.
76
+ */
77
+ export function verifyDecisionFile(file: string, scope: string): { entry?: DecisionEntry; problem?: DecisionProblem } {
78
+ const fail = (code: DecisionProblemCode, message: string) => ({ problem: { path: file, code, message } });
79
+ let raw: unknown;
80
+ try {
81
+ raw = safeYamlParse(fs.readFileSync(file, 'utf8'), file);
82
+ } catch (err) {
83
+ return fail('unreadable', err instanceof Error ? err.message : String(err));
84
+ }
85
+ const errors = storedEntryErrors(raw);
86
+ if (errors.length) return fail('invalid', errors.join('; '));
87
+ const entry = raw as DecisionEntry;
88
+ if (path.basename(file) !== `${entry.id}.yaml`) return fail('id-mismatch', `file name does not match its id ${entry.id}`);
89
+ const id = computeDecisionId(entry, scope);
90
+ if (id !== entry.id) return fail('id-mismatch', `stored id ${entry.id} but the content (in scope ${scope}) hashes to ${id}`);
91
+ if (computeEntryHash(entry) !== entry.entry_hash) return fail('hash-mismatch', 'entry_hash no longer matches the entry');
92
+ return { entry };
93
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Decision identity (DOS-13.2). Two hashes answer two different questions:
3
+ *
4
+ * id = `D-` + first 16 hex of sha256(normalized content)
5
+ * content = {kind, scope, anchors (sorted), text, reason, payload, supersedes, visibility,
6
+ * evidence_hash}; excludes valid_from, source, id, entry_hash
7
+ * → "what was decided, where": re-recording it later, by anyone, yields the same id
8
+ * entry_hash = full sha256 of the whole stored entry in canonical form (id, valid_from, source included)
9
+ * → "was this file altered": changing any field, even the date, breaks it
10
+ *
11
+ * Canonical form: keys sorted, strings NFC with LF line ends and trimmed, absent fields omitted
12
+ * (`null` in the id content so its key set is fixed). The stored entry IS the normalized form, so a
13
+ * hash over the parsed file equals a hash over what was sealed.
14
+ */
15
+ import { createHash } from 'crypto';
16
+ import { DecisionDraft, DecisionEntry, JsonValue } from './decision-schema';
17
+
18
+ const normText = (s: string): string => s.normalize('NFC').replace(/\r\n?/g, '\n').trim();
19
+
20
+ export function canonicalize(v: unknown): unknown {
21
+ if (typeof v === 'string') return normText(v);
22
+ if (Array.isArray(v)) return v.map(canonicalize);
23
+ if (v && typeof v === 'object') {
24
+ const out: Record<string, unknown> = {};
25
+ for (const k of Object.keys(v).sort()) {
26
+ const x = (v as Record<string, unknown>)[k];
27
+ if (x !== undefined) out[k] = canonicalize(x);
28
+ }
29
+ return out;
30
+ }
31
+ return v;
32
+ }
33
+
34
+ export const canonicalJson = (v: unknown): string => JSON.stringify(canonicalize(v));
35
+ const sha256 = (s: string): string => createHash('sha256').update(s).digest('hex');
36
+
37
+ /** The draft as it is stored: normalized strings, sorted anchors, absent optionals dropped. */
38
+ export function normalizeDraft(d: DecisionDraft): DecisionDraft {
39
+ const out: DecisionDraft = {
40
+ kind: d.kind,
41
+ anchors: d.anchors.map(normText).sort(),
42
+ text: normText(d.text),
43
+ reason: normText(d.reason),
44
+ ...(d.payload !== undefined ? { payload: canonicalize(d.payload) as Record<string, JsonValue> } : {}),
45
+ source: { by: normText(d.source.by), where: normText(d.source.where) },
46
+ valid_from: normText(d.valid_from),
47
+ ...(d.supersedes !== undefined ? { supersedes: normText(d.supersedes) } : {}),
48
+ visibility: d.visibility,
49
+ ...(d.evidence_hash !== undefined ? { evidence_hash: normText(d.evidence_hash) } : {}),
50
+ };
51
+ return out;
52
+ }
53
+
54
+ export function computeDecisionId(d: DecisionDraft, scope: string): string {
55
+ const n = normalizeDraft(d);
56
+ const content = {
57
+ kind: n.kind, scope, anchors: n.anchors, text: n.text, reason: n.reason, payload: n.payload ?? null,
58
+ supersedes: n.supersedes ?? null, visibility: n.visibility, evidence_hash: n.evidence_hash ?? null,
59
+ };
60
+ return `D-${sha256(canonicalJson(content)).slice(0, 16)}`;
61
+ }
62
+
63
+ export function computeEntryHash(entry: Omit<DecisionEntry, 'entry_hash'> & { entry_hash?: string }): string {
64
+ const { entry_hash: _ignored, ...rest } = entry;
65
+ return sha256(canonicalJson(rest));
66
+ }
67
+
68
+ /** Normalize a draft and give it its id and entry_hash, fields in storage order. */
69
+ export function sealDecision(d: DecisionDraft, scope: string): DecisionEntry {
70
+ const n = normalizeDraft(d);
71
+ const withId = { id: computeDecisionId(n, scope), ...n };
72
+ return { ...withId, entry_hash: computeEntryHash(withId) };
73
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Append-only integrity of the decision layer (DOS-13.3). Emits the catalog's hard rules — it does
3
+ * not define severity; phase 04's resolver decides what a finding blocks.
4
+ *
5
+ * decision.id-reuse stored id ≠ id of the content (edited text, renamed file, moved to another scope)
6
+ * decision.mutated entry_hash ≠ entry, an unreadable entry, or an entry in `git show HEAD:` that
7
+ * was edited or deleted in the working tree — the only check a careful forger
8
+ * who recomputes both hashes cannot pass
9
+ *
10
+ * Outside git (or with no commit yet) the HEAD comparison is skipped; it never throws.
11
+ */
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { safeYamlParse } from '../utils/safe-yaml-parse';
15
+ import { canonicalJson } from './decision-identity';
16
+ import { DECISION_FILE_PATH, listDecisionDirs, verifyDecisionFile } from './decision-file';
17
+ import { gitOutput as git } from './git-head-trust';
18
+
19
+ export const DECISION_INTEGRITY_RULES = { idReuse: 'decision.id-reuse', mutated: 'decision.mutated' } as const;
20
+ export type DecisionIntegrityRule = typeof DECISION_INTEGRITY_RULES[keyof typeof DECISION_INTEGRITY_RULES];
21
+ export interface DecisionFinding { rule: DecisionIntegrityRule; path: string; message: string }
22
+
23
+
24
+ /** Same meaning, whatever the line endings or YAML layout: compare the parsed entries canonically. */
25
+ function sameEntry(a: string, b: string): boolean {
26
+ try {
27
+ return canonicalJson(safeYamlParse(a)) === canonicalJson(safeYamlParse(b));
28
+ } catch {
29
+ return a.replace(/\r\n?/g, '\n') === b.replace(/\r\n?/g, '\n');
30
+ }
31
+ }
32
+
33
+ export function checkDecisionIntegrity(projectRoot: string): DecisionFinding[] {
34
+ const out = new Map<string, DecisionFinding>();
35
+ const add = (rule: DecisionIntegrityRule, abs: string, message: string): void => {
36
+ const rel = path.relative(projectRoot, abs).split(path.sep).join('/');
37
+ if (!out.has(`${rule} ${rel}`)) out.set(`${rule} ${rel}`, { rule, path: rel, message });
38
+ };
39
+
40
+ for (const { scope, dir } of listDecisionDirs(projectRoot)) {
41
+ let names: string[] = [];
42
+ try { names = fs.readdirSync(dir).filter((f) => f.endsWith('.yaml')); } catch { continue; }
43
+ for (const name of names) {
44
+ const file = path.join(dir, name);
45
+ const v = verifyDecisionFile(file, scope);
46
+ if (!v.problem) continue;
47
+ add(v.problem.code === 'id-mismatch' ? DECISION_INTEGRITY_RULES.idReuse : DECISION_INTEGRITY_RULES.mutated, file, v.problem.message);
48
+ }
49
+ }
50
+
51
+ const listed = git(projectRoot, ['ls-tree', '-r', '-z', '--name-only', 'HEAD', '--', 'qa']);
52
+ for (const rel of (listed ?? '').split('\0').filter((p) => DECISION_FILE_PATH.test(p))) {
53
+ const abs = path.join(projectRoot, ...rel.split('/'));
54
+ if (!fs.existsSync(abs)) { add(DECISION_INTEGRITY_RULES.mutated, abs, 'committed entry was deleted (append-only: supersede it instead)'); continue; }
55
+ const head = git(projectRoot, ['show', `HEAD:./${rel}`]);
56
+ if (head !== null && !sameEntry(head, fs.readFileSync(abs, 'utf8'))) {
57
+ add(DECISION_INTEGRITY_RULES.mutated, abs, 'committed entry was edited (append-only: record a superseding entry instead)');
58
+ }
59
+ }
60
+ return [...out.values()];
61
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Decision entry schema (DOS-13.2).
3
+ *
4
+ * `{id, kind, anchors[], text, reason, payload?, source:{by, where}, valid_from, supersedes?,
5
+ * visibility: internal|client, evidence_hash?, entry_hash}`
6
+ *
7
+ * Validation is strict both ways: a draft is checked before it is written, and a stored entry is
8
+ * checked when read — an unknown key (a stored `superseded_by`, say) makes the entry invalid, because
9
+ * a field outside the id's content could carry meaning the id does not vouch for.
10
+ */
11
+ export const DECISION_KINDS = ['waiver', 'gate-override', 'contract-approval', 'viewpoint-acceptance', 'clarification'] as const;
12
+ export type DecisionKind = typeof DECISION_KINDS[number];
13
+ export type DecisionVisibility = 'internal' | 'client';
14
+ export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
15
+
16
+ export interface DecisionSource { by: string; where: string }
17
+
18
+ export interface DecisionDraft {
19
+ kind: DecisionKind;
20
+ anchors: string[];
21
+ text: string;
22
+ reason: string;
23
+ payload?: Record<string, JsonValue>;
24
+ source: DecisionSource;
25
+ valid_from: string;
26
+ supersedes?: string;
27
+ visibility: DecisionVisibility;
28
+ evidence_hash?: string;
29
+ }
30
+
31
+ export interface DecisionEntry extends DecisionDraft { id: string; entry_hash: string }
32
+
33
+ export const DECISION_ID_RE = /^D-[0-9a-f]{16}$/;
34
+ const ENTRY_HASH_RE = /^[0-9a-f]{64}$/;
35
+ const ISO_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
36
+ const STORED_KEYS = new Set(['id', 'kind', 'anchors', 'text', 'reason', 'payload', 'source', 'valid_from', 'supersedes', 'visibility', 'evidence_hash', 'entry_hash']);
37
+
38
+ const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
39
+ const nonEmpty = (v: unknown): boolean => typeof v === 'string' && v.trim().length > 0;
40
+
41
+ function isJson(v: unknown, depth = 0): boolean {
42
+ if (depth > 20) return false;
43
+ if (v === null || typeof v === 'string' || typeof v === 'boolean') return true;
44
+ if (typeof v === 'number') return Number.isFinite(v);
45
+ if (Array.isArray(v)) return v.every((x) => isJson(x, depth + 1));
46
+ return isObject(v) && Object.values(v).every((x) => isJson(x, depth + 1));
47
+ }
48
+
49
+ /** Errors in a draft (empty = valid). Shared by the write boundary and the stored-entry check. */
50
+ export function draftErrors(d: Record<string, unknown>): string[] {
51
+ const e: string[] = [];
52
+ if (!DECISION_KINDS.includes(d.kind as DecisionKind)) e.push(`kind must be one of ${DECISION_KINDS.join('|')}`);
53
+ if (!Array.isArray(d.anchors) || !d.anchors.every(nonEmpty)) e.push('anchors must be a list of non-empty strings');
54
+ if (!nonEmpty(d.text)) e.push('text is required');
55
+ if (!nonEmpty(d.reason)) e.push('reason is required');
56
+ if (d.payload !== undefined && !(isObject(d.payload) && isJson(d.payload))) e.push('payload must be a JSON object');
57
+ const src = d.source;
58
+ if (!isObject(src) || typeof src.by !== 'string' || !nonEmpty(src.where) || Object.keys(src).some((k) => k !== 'by' && k !== 'where')) {
59
+ e.push('source must be {by, where}');
60
+ }
61
+ if (typeof d.valid_from !== 'string' || !ISO_RE.test(d.valid_from) || Number.isNaN(Date.parse(d.valid_from))) e.push('valid_from must be an ISO-8601 timestamp');
62
+ if (d.supersedes !== undefined && (typeof d.supersedes !== 'string' || !DECISION_ID_RE.test(d.supersedes))) e.push('supersedes must be a decision id D-<16 hex>');
63
+ if (d.visibility !== 'internal' && d.visibility !== 'client') e.push('visibility must be internal|client');
64
+ if (d.evidence_hash !== undefined && typeof d.evidence_hash !== 'string') e.push('evidence_hash must be a string');
65
+ return e;
66
+ }
67
+
68
+ /** A stored entry: the draft rules plus id, entry_hash and no unknown keys. */
69
+ export function storedEntryErrors(raw: unknown): string[] {
70
+ if (!isObject(raw)) return ['not a mapping'];
71
+ const e = draftErrors(raw);
72
+ for (const k of Object.keys(raw)) if (!STORED_KEYS.has(k)) e.push(`unknown key "${k}"`);
73
+ if (typeof raw.id !== 'string' || !DECISION_ID_RE.test(raw.id)) e.push('id must be D-<16 hex>');
74
+ if (typeof raw.entry_hash !== 'string' || !ENTRY_HASH_RE.test(raw.entry_hash)) e.push('entry_hash must be 64 hex');
75
+ return e;
76
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Who recorded a decision: `git config user.name` — a name, never the email (DOS-13.2, security
3
+ * considerations). Best-effort: no git, no name → 'unknown'.
4
+ */
5
+ import { execFileSync } from 'child_process';
6
+
7
+ export function gitUserName(cwd: string): string {
8
+ try {
9
+ const name = execFileSync('git', ['config', 'user.name'], { cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 5000 }).toString('utf8').trim();
10
+ return name || 'unknown';
11
+ } catch {
12
+ return 'unknown';
13
+ }
14
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Create a file only if its name is free, and never leave it half-written (red-team #13).
3
+ *
4
+ * The bytes go to a private tmp file opened with O_EXCL, are fsynced, then hard-linked to the final
5
+ * name. `link` fails with EEXIST when the name is taken, so the final file appears complete or not at
6
+ * all. On a filesystem without hard links an O_EXCL create of the final name keeps the exclusivity.
7
+ * Used for decision entries (same content → same name → "already recorded") and for the migration
8
+ * sentinel (two migrations of one unit cannot both believe they hold it).
9
+ */
10
+ import { randomBytes } from 'crypto';
11
+ import * as fs from 'fs';
12
+
13
+ const NO_HARD_LINKS = new Set(['EPERM', 'ENOTSUP', 'EOPNOTSUPP', 'ENOSYS', 'EXDEV']);
14
+
15
+ /** true = created, false = the name was already taken. Any other failure throws. */
16
+ export function createFileExclusive(file: string, text: string): boolean {
17
+ const tmp = `${file}.tmp-${process.pid}-${randomBytes(6).toString('hex')}`;
18
+ const fd = fs.openSync(tmp, 'wx');
19
+ try {
20
+ fs.writeFileSync(fd, text);
21
+ fs.fsyncSync(fd);
22
+ } finally {
23
+ fs.closeSync(fd);
24
+ }
25
+ try {
26
+ fs.linkSync(tmp, file);
27
+ return true;
28
+ } catch (err) {
29
+ const code = (err as NodeJS.ErrnoException).code ?? '';
30
+ if (code === 'EEXIST') return false;
31
+ if (!NO_HARD_LINKS.has(code)) throw err;
32
+ try {
33
+ fs.writeFileSync(file, text, { flag: 'wx' });
34
+ return true;
35
+ } catch (e) {
36
+ if ((e as NodeJS.ErrnoException).code === 'EEXIST') return false;
37
+ throw e;
38
+ }
39
+ } finally {
40
+ fs.rmSync(tmp, { force: true });
41
+ }
42
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * What git HEAD says — the trust anchor for agent-writable state (red-team #3, review H1).
3
+ *
4
+ * `.sungen/` files are written by the CLI and, without the guard, by any agent. A record in them is
5
+ * trusted only where it equals its committed version: a commit went through review, a local edit
6
+ * did not. Outside git, or for a file git does not track, nothing is trusted. Never throws.
7
+ */
8
+ import { execFileSync } from 'child_process';
9
+ import * as path from 'path';
10
+
11
+ /** stdout of a git command run in `cwd`, or null when git fails (no repo, no HEAD, untracked path…). */
12
+ export function gitOutput(cwd: string, args: string[]): string | null {
13
+ try {
14
+ return execFileSync('git', args, { cwd, stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 * 1024 * 1024, timeout: 30000 }).toString('utf8');
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ /** The committed text of `abs` (a file inside `projectRoot`), or null when git HEAD has no such file. */
21
+ export function committedText(projectRoot: string, abs: string): string | null {
22
+ const rel = path.relative(projectRoot, abs).split(path.sep).join('/');
23
+ if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return null;
24
+ return gitOutput(projectRoot, ['show', `HEAD:./${rel}`]);
25
+ }
26
+
27
+ /** The committed JSON of `abs`, or null (absent from HEAD, or not JSON). */
28
+ export function committedJson(projectRoot: string, abs: string): unknown {
29
+ const text = committedText(projectRoot, abs);
30
+ if (text === null) return null;
31
+ try { return JSON.parse(text.replace(/^/, '')); } catch { return null; }
32
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Typed human confirmation (DOS-13.4, red-team #3; review phase 03 M5).
3
+ *
4
+ * `journey --waive`, `journey --decide`, `delivery --accept-in-run` record or accept decisions that
5
+ * relax a gate, so a person must type a confirmation on a terminal:
6
+ * - no TTY (an agent's shell, a pipe, CI) → refuse and print the exact command for a human to run;
7
+ * piped stdin is not a TTY, so piping the phrase in never counts
8
+ * - the prompt and the answer go through the controlling terminal (`/dev/tty`), not stdin/stderr,
9
+ * so a redirected or wrapped stream neither sees nor answers it
10
+ * - the phrase carries a NONCE drawn per prompt, so an answer cannot be scripted from the refusal
11
+ * message: the printed command never contains it (it does not exist until the prompt is shown)
12
+ * This is a hard floor under Claude Code (the PreToolUse guard also denies agent writes to
13
+ * qa/**\/decisions/**) and a soft barrier elsewhere; git-HEAD comparison and the in-run rule remain.
14
+ */
15
+ import { randomBytes } from 'crypto';
16
+ import * as fs from 'fs';
17
+ import * as readline from 'readline';
18
+ import * as tty from 'tty';
19
+
20
+ export interface ConfirmationIO {
21
+ input: NodeJS.ReadableStream & { isTTY?: boolean };
22
+ output: NodeJS.WritableStream;
23
+ /** The CLI's real terminal: prompt on `/dev/tty` (opened only when a confirmation is asked). */
24
+ controllingTerminal?: boolean;
25
+ }
26
+
27
+ export class HumanConfirmationRefused extends Error {
28
+ constructor(message: string, public readonly command?: string) {
29
+ super(message);
30
+ this.name = 'HumanConfirmationRefused';
31
+ }
32
+ }
33
+
34
+ /** The CLI's streams: stdin decides whether a human is there; the prompt itself uses /dev/tty. */
35
+ export const processIO = (): ConfirmationIO => ({ input: process.stdin, output: process.stderr, controllingTerminal: true });
36
+ export const isInteractive = (io: ConfirmationIO): boolean => io.input.isTTY === true;
37
+
38
+ export function shellQuote(arg: string): string {
39
+ return /^[A-Za-z0-9_./:=@%+-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, `'\\''`)}'`;
40
+ }
41
+ export const formatCommand = (argv: string[]): string => argv.map(shellQuote).join(' ');
42
+
43
+ interface Terminal { input: NodeJS.ReadableStream; output: NodeJS.WritableStream; close(): void }
44
+
45
+ /** The terminal to talk to a human on, or null when there is none. */
46
+ function openTerminal(io: ConfirmationIO): Terminal | null {
47
+ if (!isInteractive(io)) return null;
48
+ if (!io.controllingTerminal || process.platform === 'win32') return { input: io.input, output: io.output, close: () => undefined };
49
+ let rfd = -1;
50
+ let wfd = -1;
51
+ try {
52
+ rfd = fs.openSync('/dev/tty', 'r');
53
+ wfd = fs.openSync('/dev/tty', 'w');
54
+ const input = new tty.ReadStream(rfd);
55
+ const output = new tty.WriteStream(wfd);
56
+ return { input, output, close: () => { input.destroy(); output.destroy(); } };
57
+ } catch {
58
+ for (const fd of [rfd, wfd]) if (fd >= 0) { try { fs.closeSync(fd); } catch { /* already closed */ } }
59
+ return null; // no controlling terminal: not a human at a keyboard
60
+ }
61
+ }
62
+
63
+ /** One line from the human, or null on EOF. */
64
+ function askLine(t: Terminal, prompt: string): Promise<string | null> {
65
+ return new Promise((resolve) => {
66
+ const rl = readline.createInterface({ input: t.input, output: t.output, terminal: false });
67
+ let settled = false;
68
+ const done = (v: string | null): void => { if (!settled) { settled = true; resolve(v); rl.close(); } };
69
+ rl.on('close', () => done(null));
70
+ rl.question(prompt, (answer) => done(answer));
71
+ });
72
+ }
73
+
74
+ function refuseWithoutTty(what: string, command: string): HumanConfirmationRefused {
75
+ return new HumanConfirmationRefused(
76
+ `Refused: ${what} needs a human at a terminal to type a confirmation, and this run has no TTY.\n`
77
+ + ` Nothing was recorded. Run this yourself in a terminal:\n ${command}`,
78
+ command,
79
+ );
80
+ }
81
+
82
+ /** A fresh, hard-to-guess suffix for one prompt. */
83
+ export const confirmationNonce = (): string => randomBytes(3).toString('hex').toUpperCase();
84
+
85
+ export async function requireTypedConfirmation(opts: {
86
+ what: string; phrase: string; command: string; summary: string[]; io: ConfirmationIO;
87
+ }): Promise<void> {
88
+ const t = openTerminal(opts.io);
89
+ if (!t) throw refuseWithoutTty(opts.what, opts.command);
90
+ try {
91
+ const expected = `${opts.phrase} ${confirmationNonce()}`;
92
+ t.output.write(`\nAbout to ${opts.what}:\n${opts.summary.map((l) => ` ${l}`).join('\n')}\n`);
93
+ const answer = await askLine(t, `Type "${expected}" to confirm (anything else cancels): `);
94
+ if ((answer ?? '').trim() !== expected) {
95
+ throw new HumanConfirmationRefused(`Cancelled: the confirmation did not match the phrase shown. Nothing was recorded.`, opts.command);
96
+ }
97
+ } finally {
98
+ t.close();
99
+ }
100
+ }
101
+
102
+ /** A leftover write-ahead sentinel: the human answers REVERT or KEEP. Never answered for them. */
103
+ export async function askMigrationChoice(opts: { detail: string; command: string; io: ConfirmationIO }): Promise<'REVERT' | 'KEEP'> {
104
+ const t = openTerminal(opts.io);
105
+ if (!t) throw refuseWithoutTty('answering an interrupted migration (REVERT or KEEP)', opts.command);
106
+ try {
107
+ t.output.write(`\n${opts.detail}\n`);
108
+ const answer = ((await askLine(t, 'Type REVERT (restore every file it lists) or KEEP (keep the new files): ')) ?? '').trim().toUpperCase();
109
+ if (answer === 'REVERT' || answer === 'KEEP') return answer;
110
+ throw new HumanConfirmationRefused('Cancelled: answer REVERT or KEEP. Nothing was changed.', opts.command);
111
+ } finally {
112
+ t.close();
113
+ }
114
+ }