@sun-asterisk/sungen 3.2.35 → 3.3.0-alpha.10

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 (969) 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/case-triage-hint.d.ts +14 -0
  41. package/dist/cli/case-triage-hint.d.ts.map +1 -0
  42. package/dist/cli/case-triage-hint.js +23 -0
  43. package/dist/cli/case-triage-hint.js.map +1 -0
  44. package/dist/cli/commands/audit.js +1 -1
  45. package/dist/cli/commands/audit.js.map +1 -1
  46. package/dist/cli/commands/capability.js +1 -1
  47. package/dist/cli/commands/capability.js.map +1 -1
  48. package/dist/cli/commands/contract-derive-command.d.ts +12 -0
  49. package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
  50. package/dist/cli/commands/contract-derive-command.js +113 -0
  51. package/dist/cli/commands/contract-derive-command.js.map +1 -0
  52. package/dist/cli/commands/contract.d.ts +20 -0
  53. package/dist/cli/commands/contract.d.ts.map +1 -0
  54. package/dist/cli/commands/contract.js +195 -0
  55. package/dist/cli/commands/contract.js.map +1 -0
  56. package/dist/cli/commands/delivery.d.ts.map +1 -1
  57. package/dist/cli/commands/delivery.js +109 -19
  58. package/dist/cli/commands/delivery.js.map +1 -1
  59. package/dist/cli/commands/gate.d.ts.map +1 -1
  60. package/dist/cli/commands/gate.js +2 -1
  61. package/dist/cli/commands/gate.js.map +1 -1
  62. package/dist/cli/commands/generate.d.ts.map +1 -1
  63. package/dist/cli/commands/generate.js +17 -3
  64. package/dist/cli/commands/generate.js.map +1 -1
  65. package/dist/cli/commands/journey.d.ts.map +1 -1
  66. package/dist/cli/commands/journey.js +47 -17
  67. package/dist/cli/commands/journey.js.map +1 -1
  68. package/dist/cli/commands/next.d.ts +13 -0
  69. package/dist/cli/commands/next.d.ts.map +1 -1
  70. package/dist/cli/commands/next.js +98 -9
  71. package/dist/cli/commands/next.js.map +1 -1
  72. package/dist/cli/commands/triage.d.ts +2 -1
  73. package/dist/cli/commands/triage.d.ts.map +1 -1
  74. package/dist/cli/commands/triage.js +45 -3
  75. package/dist/cli/commands/triage.js.map +1 -1
  76. package/dist/cli/commands/update.d.ts +11 -0
  77. package/dist/cli/commands/update.d.ts.map +1 -1
  78. package/dist/cli/commands/update.js +37 -8
  79. package/dist/cli/commands/update.js.map +1 -1
  80. package/dist/cli/index.js +24 -33
  81. package/dist/cli/index.js.map +1 -1
  82. package/dist/cli/json-error-on-exit.d.ts +34 -0
  83. package/dist/cli/json-error-on-exit.d.ts.map +1 -0
  84. package/dist/cli/json-error-on-exit.js +63 -0
  85. package/dist/cli/json-error-on-exit.js.map +1 -0
  86. package/dist/cli/project-sungen-invocation.d.ts +13 -0
  87. package/dist/cli/project-sungen-invocation.d.ts.map +1 -0
  88. package/dist/cli/project-sungen-invocation.js +95 -0
  89. package/dist/cli/project-sungen-invocation.js.map +1 -0
  90. package/dist/cli/run-command-hints.d.ts +9 -0
  91. package/dist/cli/run-command-hints.d.ts.map +1 -0
  92. package/dist/cli/run-command-hints.js +84 -0
  93. package/dist/cli/run-command-hints.js.map +1 -0
  94. package/dist/cli/waiting-for-you-block.d.ts +18 -0
  95. package/dist/cli/waiting-for-you-block.d.ts.map +1 -0
  96. package/dist/cli/waiting-for-you-block.js +22 -0
  97. package/dist/cli/waiting-for-you-block.js.map +1 -0
  98. package/dist/contract/accept-opaque-steps.d.ts +3 -0
  99. package/dist/contract/accept-opaque-steps.d.ts.map +1 -0
  100. package/dist/contract/accept-opaque-steps.js +114 -0
  101. package/dist/contract/accept-opaque-steps.js.map +1 -0
  102. package/dist/contract/approve-contract.d.ts +38 -0
  103. package/dist/contract/approve-contract.d.ts.map +1 -0
  104. package/dist/contract/approve-contract.js +93 -0
  105. package/dist/contract/approve-contract.js.map +1 -0
  106. package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
  107. package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
  108. package/dist/contract/assertion-matcher-vocabulary.js +100 -0
  109. package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
  110. package/dist/contract/build-contract-verdict.d.ts +32 -0
  111. package/dist/contract/build-contract-verdict.d.ts.map +1 -0
  112. package/dist/contract/build-contract-verdict.js +123 -0
  113. package/dist/contract/build-contract-verdict.js.map +1 -0
  114. package/dist/contract/build-trace-conformance.d.ts +22 -0
  115. package/dist/contract/build-trace-conformance.d.ts.map +1 -0
  116. package/dist/contract/build-trace-conformance.js +151 -0
  117. package/dist/contract/build-trace-conformance.js.map +1 -0
  118. package/dist/contract/classify-contract-change.d.ts +24 -0
  119. package/dist/contract/classify-contract-change.d.ts.map +1 -0
  120. package/dist/contract/classify-contract-change.js +98 -0
  121. package/dist/contract/classify-contract-change.js.map +1 -0
  122. package/dist/contract/classify-contract-unit.d.ts +18 -0
  123. package/dist/contract/classify-contract-unit.d.ts.map +1 -0
  124. package/dist/contract/classify-contract-unit.js +49 -0
  125. package/dist/contract/classify-contract-unit.js.map +1 -0
  126. package/dist/contract/classify-trace-conformance.d.ts +47 -0
  127. package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
  128. package/dist/contract/classify-trace-conformance.js +164 -0
  129. package/dist/contract/classify-trace-conformance.js.map +1 -0
  130. package/dist/contract/contract-git.d.ts +24 -0
  131. package/dist/contract/contract-git.d.ts.map +1 -0
  132. package/dist/contract/contract-git.js +144 -0
  133. package/dist/contract/contract-git.js.map +1 -0
  134. package/dist/contract/contract-hash.d.ts +7 -0
  135. package/dist/contract/contract-hash.d.ts.map +1 -0
  136. package/dist/contract/contract-hash.js +38 -0
  137. package/dist/contract/contract-hash.js.map +1 -0
  138. package/dist/contract/contract-report-file.d.ts +4 -0
  139. package/dist/contract/contract-report-file.d.ts.map +1 -0
  140. package/dist/contract/contract-report-file.js +60 -0
  141. package/dist/contract/contract-report-file.js.map +1 -0
  142. package/dist/contract/contract-schema.d.ts +92 -0
  143. package/dist/contract/contract-schema.d.ts.map +1 -0
  144. package/dist/contract/contract-schema.js +124 -0
  145. package/dist/contract/contract-schema.js.map +1 -0
  146. package/dist/contract/contract-units.d.ts +12 -0
  147. package/dist/contract/contract-units.d.ts.map +1 -0
  148. package/dist/contract/contract-units.js +96 -0
  149. package/dist/contract/contract-units.js.map +1 -0
  150. package/dist/contract/contracts-lock-file.d.ts +24 -0
  151. package/dist/contract/contracts-lock-file.d.ts.map +1 -0
  152. package/dist/contract/contracts-lock-file.js +141 -0
  153. package/dist/contract/contracts-lock-file.js.map +1 -0
  154. package/dist/contract/data-normalize.d.ts +10 -0
  155. package/dist/contract/data-normalize.d.ts.map +1 -0
  156. package/dist/contract/data-normalize.js +110 -0
  157. package/dist/contract/data-normalize.js.map +1 -0
  158. package/dist/contract/derive-free-feature.d.ts +49 -0
  159. package/dist/contract/derive-free-feature.d.ts.map +1 -0
  160. package/dist/contract/derive-free-feature.js +183 -0
  161. package/dist/contract/derive-free-feature.js.map +1 -0
  162. package/dist/contract/field-semantic-redaction.d.ts +21 -0
  163. package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
  164. package/dist/contract/field-semantic-redaction.js +118 -0
  165. package/dist/contract/field-semantic-redaction.js.map +1 -0
  166. package/dist/contract/free-feature-blocks.d.ts +10 -0
  167. package/dist/contract/free-feature-blocks.d.ts.map +1 -0
  168. package/dist/contract/free-feature-blocks.js +94 -0
  169. package/dist/contract/free-feature-blocks.js.map +1 -0
  170. package/dist/contract/lock-secret-guard.d.ts +22 -0
  171. package/dist/contract/lock-secret-guard.d.ts.map +1 -0
  172. package/dist/contract/lock-secret-guard.js +134 -0
  173. package/dist/contract/lock-secret-guard.js.map +1 -0
  174. package/dist/contract/lock-vs-git-head.d.ts +26 -0
  175. package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
  176. package/dist/contract/lock-vs-git-head.js +28 -0
  177. package/dist/contract/lock-vs-git-head.js.map +1 -0
  178. package/dist/contract/normalize-contract-step.d.ts +21 -0
  179. package/dist/contract/normalize-contract-step.d.ts.map +1 -0
  180. package/dist/contract/normalize-contract-step.js +70 -0
  181. package/dist/contract/normalize-contract-step.js.map +1 -0
  182. package/dist/contract/normalize-feature-steps.d.ts +7 -0
  183. package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
  184. package/dist/contract/normalize-feature-steps.js +127 -0
  185. package/dist/contract/normalize-feature-steps.js.map +1 -0
  186. package/dist/contract/oracle-strength.d.ts +12 -0
  187. package/dist/contract/oracle-strength.d.ts.map +1 -0
  188. package/dist/contract/oracle-strength.js +88 -0
  189. package/dist/contract/oracle-strength.js.map +1 -0
  190. package/dist/contract/project-contract-blockers.d.ts +10 -0
  191. package/dist/contract/project-contract-blockers.d.ts.map +1 -0
  192. package/dist/contract/project-contract-blockers.js +75 -0
  193. package/dist/contract/project-contract-blockers.js.map +1 -0
  194. package/dist/contract/record-contract-approval.d.ts +36 -0
  195. package/dist/contract/record-contract-approval.d.ts.map +1 -0
  196. package/dist/contract/record-contract-approval.js +126 -0
  197. package/dist/contract/record-contract-approval.js.map +1 -0
  198. package/dist/contract/render-free-gherkin.d.ts +42 -0
  199. package/dist/contract/render-free-gherkin.d.ts.map +1 -0
  200. package/dist/contract/render-free-gherkin.js +147 -0
  201. package/dist/contract/render-free-gherkin.js.map +1 -0
  202. package/dist/contract/render-trace-sentence.d.ts +20 -0
  203. package/dist/contract/render-trace-sentence.d.ts.map +1 -0
  204. package/dist/contract/render-trace-sentence.js +109 -0
  205. package/dist/contract/render-trace-sentence.js.map +1 -0
  206. package/dist/contract/render-trace-target.d.ts +14 -0
  207. package/dist/contract/render-trace-target.d.ts.map +1 -0
  208. package/dist/contract/render-trace-target.js +70 -0
  209. package/dist/contract/render-trace-target.js.map +1 -0
  210. package/dist/contract/resolve-unit-trace-identities.d.ts +20 -0
  211. package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
  212. package/dist/contract/resolve-unit-trace-identities.js +62 -0
  213. package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
  214. package/dist/contract/trace/collect-trace-evidence.d.ts +44 -0
  215. package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
  216. package/dist/contract/trace/collect-trace-evidence.js +219 -0
  217. package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
  218. package/dist/contract/trace/identity-cache.d.ts +19 -0
  219. package/dist/contract/trace/identity-cache.d.ts.map +1 -0
  220. package/dist/contract/trace/identity-cache.js +104 -0
  221. package/dist/contract/trace/identity-cache.js.map +1 -0
  222. package/dist/contract/trace/read-file-once.d.ts +20 -0
  223. package/dist/contract/trace/read-file-once.d.ts.map +1 -0
  224. package/dist/contract/trace/read-file-once.js +135 -0
  225. package/dist/contract/trace/read-file-once.js.map +1 -0
  226. package/dist/contract/trace/scenario-id-claims.d.ts +11 -0
  227. package/dist/contract/trace/scenario-id-claims.d.ts.map +1 -0
  228. package/dist/contract/trace/scenario-id-claims.js +89 -0
  229. package/dist/contract/trace/scenario-id-claims.js.map +1 -0
  230. package/dist/contract/trace/select-final-attempt.d.ts +29 -0
  231. package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
  232. package/dist/contract/trace/select-final-attempt.js +119 -0
  233. package/dist/contract/trace/select-final-attempt.js.map +1 -0
  234. package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
  235. package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
  236. package/dist/contract/trace/snapshot-html-renderer.js +172 -0
  237. package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
  238. package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
  239. package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
  240. package/dist/contract/trace/snapshot-resolver.js +65 -0
  241. package/dist/contract/trace/snapshot-resolver.js.map +1 -0
  242. package/dist/contract/trace/snapshot-store.d.ts +66 -0
  243. package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
  244. package/dist/contract/trace/snapshot-store.js +173 -0
  245. package/dist/contract/trace/snapshot-store.js.map +1 -0
  246. package/dist/contract/trace/target-identity.d.ts +74 -0
  247. package/dist/contract/trace/target-identity.d.ts.map +1 -0
  248. package/dist/contract/trace/target-identity.js +68 -0
  249. package/dist/contract/trace/target-identity.js.map +1 -0
  250. package/dist/contract/trace/test-step-join.d.ts +65 -0
  251. package/dist/contract/trace/test-step-join.d.ts.map +1 -0
  252. package/dist/contract/trace/test-step-join.js +80 -0
  253. package/dist/contract/trace/test-step-join.js.map +1 -0
  254. package/dist/contract/trace/trace-binding.d.ts +22 -0
  255. package/dist/contract/trace/trace-binding.d.ts.map +1 -0
  256. package/dist/contract/trace/trace-binding.js +91 -0
  257. package/dist/contract/trace/trace-binding.js.map +1 -0
  258. package/dist/contract/trace/trace-file-reader.d.ts +47 -0
  259. package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
  260. package/dist/contract/trace/trace-file-reader.js +137 -0
  261. package/dist/contract/trace/trace-file-reader.js.map +1 -0
  262. package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
  263. package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
  264. package/dist/contract/trace/trace-fingerprint.js +96 -0
  265. package/dist/contract/trace/trace-fingerprint.js.map +1 -0
  266. package/dist/contract/trace/trace-freshness.d.ts +27 -0
  267. package/dist/contract/trace/trace-freshness.d.ts.map +1 -0
  268. package/dist/contract/trace/trace-freshness.js +146 -0
  269. package/dist/contract/trace/trace-freshness.js.map +1 -0
  270. package/dist/contract/trace/trace-identity-resolution.d.ts +28 -0
  271. package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
  272. package/dist/contract/trace/trace-identity-resolution.js +87 -0
  273. package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
  274. package/dist/contract/trace/trace-run-record.d.ts +32 -0
  275. package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
  276. package/dist/contract/trace/trace-run-record.js +135 -0
  277. package/dist/contract/trace/trace-run-record.js.map +1 -0
  278. package/dist/contract/trace/trace-steps.d.ts +19 -0
  279. package/dist/contract/trace/trace-steps.d.ts.map +1 -0
  280. package/dist/contract/trace/trace-steps.js +79 -0
  281. package/dist/contract/trace/trace-steps.js.map +1 -0
  282. package/dist/contract/trace/trace-zip-limits.d.ts +45 -0
  283. package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
  284. package/dist/contract/trace/trace-zip-limits.js +52 -0
  285. package/dist/contract/trace/trace-zip-limits.js.map +1 -0
  286. package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
  287. package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
  288. package/dist/contract/trace/trace-zip-reader.js +186 -0
  289. package/dist/contract/trace/trace-zip-reader.js.map +1 -0
  290. package/dist/contract/verify-approval-decisions.d.ts +5 -0
  291. package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
  292. package/dist/contract/verify-approval-decisions.js +109 -0
  293. package/dist/contract/verify-approval-decisions.js.map +1 -0
  294. package/dist/decisions/append-decision.d.ts +12 -0
  295. package/dist/decisions/append-decision.d.ts.map +1 -0
  296. package/dist/decisions/append-decision.js +73 -0
  297. package/dist/decisions/append-decision.js.map +1 -0
  298. package/dist/decisions/confirmed-decision-commands.d.ts +41 -0
  299. package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
  300. package/dist/decisions/confirmed-decision-commands.js +190 -0
  301. package/dist/decisions/confirmed-decision-commands.js.map +1 -0
  302. package/dist/decisions/decision-file.d.ts +31 -0
  303. package/dist/decisions/decision-file.d.ts.map +1 -0
  304. package/dist/decisions/decision-file.js +139 -0
  305. package/dist/decisions/decision-file.js.map +1 -0
  306. package/dist/decisions/decision-identity.d.ts +12 -0
  307. package/dist/decisions/decision-identity.d.ts.map +1 -0
  308. package/dist/decisions/decision-identity.js +78 -0
  309. package/dist/decisions/decision-identity.js.map +1 -0
  310. package/dist/decisions/decision-integrity-check.d.ts +12 -0
  311. package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
  312. package/dist/decisions/decision-integrity-check.js +102 -0
  313. package/dist/decisions/decision-integrity-check.js.map +1 -0
  314. package/dist/decisions/decision-schema.d.ts +43 -0
  315. package/dist/decisions/decision-schema.d.ts.map +1 -0
  316. package/dist/decisions/decision-schema.js +76 -0
  317. package/dist/decisions/decision-schema.js.map +1 -0
  318. package/dist/decisions/decision-source.d.ts +2 -0
  319. package/dist/decisions/decision-source.d.ts.map +1 -0
  320. package/dist/decisions/decision-source.js +18 -0
  321. package/dist/decisions/decision-source.js.map +1 -0
  322. package/dist/decisions/exclusive-file-create.d.ts +3 -0
  323. package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
  324. package/dist/decisions/exclusive-file-create.js +84 -0
  325. package/dist/decisions/exclusive-file-create.js.map +1 -0
  326. package/dist/decisions/git-head-trust.d.ts +19 -0
  327. package/dist/decisions/git-head-trust.d.ts.map +1 -0
  328. package/dist/decisions/git-head-trust.js +128 -0
  329. package/dist/decisions/git-head-trust.js.map +1 -0
  330. package/dist/decisions/human-confirmation.d.ts +35 -0
  331. package/dist/decisions/human-confirmation.d.ts.map +1 -0
  332. package/dist/decisions/human-confirmation.js +161 -0
  333. package/dist/decisions/human-confirmation.js.map +1 -0
  334. package/dist/decisions/legacy-waiver-migration.d.ts +38 -0
  335. package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
  336. package/dist/decisions/legacy-waiver-migration.js +179 -0
  337. package/dist/decisions/legacy-waiver-migration.js.map +1 -0
  338. package/dist/decisions/read-decisions.d.ts +19 -0
  339. package/dist/decisions/read-decisions.d.ts.map +1 -0
  340. package/dist/decisions/read-decisions.js +90 -0
  341. package/dist/decisions/read-decisions.js.map +1 -0
  342. package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
  343. package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
  344. package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
  345. package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
  346. package/dist/decisions/waiver-decisions.d.ts +24 -0
  347. package/dist/decisions/waiver-decisions.d.ts.map +1 -0
  348. package/dist/decisions/waiver-decisions.js +46 -0
  349. package/dist/decisions/waiver-decisions.js.map +1 -0
  350. package/dist/exporters/csv-exporter.d.ts +11 -2
  351. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  352. package/dist/exporters/csv-exporter.js +8 -4
  353. package/dist/exporters/csv-exporter.js.map +1 -1
  354. package/dist/exporters/matrix/build.d.ts +3 -0
  355. package/dist/exporters/matrix/build.d.ts.map +1 -1
  356. package/dist/exporters/matrix/build.js +10 -9
  357. package/dist/exporters/matrix/build.js.map +1 -1
  358. package/dist/exporters/matrix/export.d.ts +9 -1
  359. package/dist/exporters/matrix/export.d.ts.map +1 -1
  360. package/dist/exporters/matrix/export.js +13 -4
  361. package/dist/exporters/matrix/export.js.map +1 -1
  362. package/dist/exporters/matrix/gates.d.ts +6 -1
  363. package/dist/exporters/matrix/gates.d.ts.map +1 -1
  364. package/dist/exporters/matrix/gates.js +57 -128
  365. package/dist/exporters/matrix/gates.js.map +1 -1
  366. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  367. package/dist/exporters/matrix/render-csv.js +3 -0
  368. package/dist/exporters/matrix/render-csv.js.map +1 -1
  369. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
  370. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
  371. package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
  372. package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
  373. package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
  374. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  375. package/dist/exporters/matrix/render-xlsx.js +3 -0
  376. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  377. package/dist/exporters/matrix/types.d.ts +4 -1
  378. package/dist/exporters/matrix/types.d.ts.map +1 -1
  379. package/dist/exporters/matrix/types.js.map +1 -1
  380. package/dist/exporters/matrix/xlsx-cells.js +1 -1
  381. package/dist/exporters/matrix/xlsx-cells.js.map +1 -1
  382. package/dist/exporters/test-data-resolver.d.ts.map +1 -1
  383. package/dist/exporters/test-data-resolver.js +2 -12
  384. package/dist/exporters/test-data-resolver.js.map +1 -1
  385. package/dist/exporters/xlsx-exporter.d.ts +3 -2
  386. package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
  387. package/dist/exporters/xlsx-exporter.js +4 -4
  388. package/dist/exporters/xlsx-exporter.js.map +1 -1
  389. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  390. package/dist/generators/gherkin-parser/index.js +0 -1
  391. package/dist/generators/gherkin-parser/index.js.map +1 -1
  392. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +1 -2
  393. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -13
  394. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -9
  395. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  396. package/dist/generators/test-generator/code-generator.js +18 -4
  397. package/dist/generators/test-generator/code-generator.js.map +1 -1
  398. package/dist/generators/test-generator/diagnostics.d.ts +3 -9
  399. package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
  400. package/dist/generators/test-generator/diagnostics.js +3 -9
  401. package/dist/generators/test-generator/diagnostics.js.map +1 -1
  402. package/dist/generators/test-generator/patterns/types.d.ts +1 -1
  403. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  404. package/dist/generators/test-generator/step-mapper.d.ts +0 -8
  405. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  406. package/dist/generators/test-generator/step-mapper.js +8 -52
  407. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  408. package/dist/generators/test-generator/utils/data-resolver.d.ts +0 -16
  409. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  410. package/dist/generators/test-generator/utils/data-resolver.js +0 -59
  411. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  412. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  413. package/dist/generators/test-generator/utils/selector-resolver.js +2 -10
  414. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  415. package/dist/graph/board-human-actions.d.ts +5 -0
  416. package/dist/graph/board-human-actions.d.ts.map +1 -0
  417. package/dist/graph/board-human-actions.js +83 -0
  418. package/dist/graph/board-human-actions.js.map +1 -0
  419. package/dist/graph/evaluate-graph.d.ts +41 -0
  420. package/dist/graph/evaluate-graph.d.ts.map +1 -0
  421. package/dist/graph/evaluate-graph.js +77 -0
  422. package/dist/graph/evaluate-graph.js.map +1 -0
  423. package/dist/graph/human-action.d.ts +21 -0
  424. package/dist/graph/human-action.d.ts.map +1 -0
  425. package/dist/graph/human-action.js +3 -0
  426. package/dist/graph/human-action.js.map +1 -0
  427. package/dist/graph/locate-finding.d.ts +20 -0
  428. package/dist/graph/locate-finding.d.ts.map +1 -0
  429. package/dist/graph/locate-finding.js +137 -0
  430. package/dist/graph/locate-finding.js.map +1 -0
  431. package/dist/graph/manual-triage-decisions.d.ts +19 -0
  432. package/dist/graph/manual-triage-decisions.d.ts.map +1 -0
  433. package/dist/graph/manual-triage-decisions.js +53 -0
  434. package/dist/graph/manual-triage-decisions.js.map +1 -0
  435. package/dist/graph/node-decl.d.ts +75 -0
  436. package/dist/graph/node-decl.d.ts.map +1 -0
  437. package/dist/graph/node-decl.js +11 -0
  438. package/dist/graph/node-decl.js.map +1 -0
  439. package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
  440. package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
  441. package/dist/graph/nodes/core-node-catalog.js +13 -0
  442. package/dist/graph/nodes/core-node-catalog.js.map +1 -0
  443. package/dist/graph/nodes/test-case-contract-nodes.d.ts +21 -0
  444. package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
  445. package/dist/graph/nodes/test-case-contract-nodes.js +72 -0
  446. package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
  447. package/dist/graph/nodes/test-case-nodes.d.ts +29 -0
  448. package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
  449. package/dist/graph/nodes/test-case-nodes.js +95 -0
  450. package/dist/graph/nodes/test-case-nodes.js.map +1 -0
  451. package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
  452. package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
  453. package/dist/graph/nodes/unit-audit-nodes.js +96 -0
  454. package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
  455. package/dist/graph/nodes/unit-contract-node.d.ts +21 -0
  456. package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
  457. package/dist/graph/nodes/unit-contract-node.js +96 -0
  458. package/dist/graph/nodes/unit-contract-node.js.map +1 -0
  459. package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
  460. package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
  461. package/dist/graph/nodes/unit-design-nodes.js +91 -0
  462. package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
  463. package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
  464. package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
  465. package/dist/graph/nodes/unit-execution-nodes.js +79 -0
  466. package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
  467. package/dist/graph/test-case-identity.d.ts +13 -0
  468. package/dist/graph/test-case-identity.d.ts.map +1 -0
  469. package/dist/graph/test-case-identity.js +106 -0
  470. package/dist/graph/test-case-identity.js.map +1 -0
  471. package/dist/graph/test-case-results.d.ts +12 -0
  472. package/dist/graph/test-case-results.d.ts.map +1 -0
  473. package/dist/graph/test-case-results.js +161 -0
  474. package/dist/graph/test-case-results.js.map +1 -0
  475. package/dist/graph/test-case-satisfied.d.ts +26 -0
  476. package/dist/graph/test-case-satisfied.d.ts.map +1 -0
  477. package/dist/graph/test-case-satisfied.js +55 -0
  478. package/dist/graph/test-case-satisfied.js.map +1 -0
  479. package/dist/graph/test-case-state-shape.d.ts +73 -0
  480. package/dist/graph/test-case-state-shape.d.ts.map +1 -0
  481. package/dist/graph/test-case-state-shape.js +3 -0
  482. package/dist/graph/test-case-state-shape.js.map +1 -0
  483. package/dist/graph/test-case-states.d.ts +9 -0
  484. package/dist/graph/test-case-states.d.ts.map +1 -0
  485. package/dist/graph/test-case-states.js +116 -0
  486. package/dist/graph/test-case-states.js.map +1 -0
  487. package/dist/graph/unit-artifact-probes.d.ts +19 -0
  488. package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
  489. package/dist/graph/unit-artifact-probes.js +135 -0
  490. package/dist/graph/unit-artifact-probes.js.map +1 -0
  491. package/dist/graph/unit-results-freshness.d.ts +29 -0
  492. package/dist/graph/unit-results-freshness.d.ts.map +1 -0
  493. package/dist/graph/unit-results-freshness.js +213 -0
  494. package/dist/graph/unit-results-freshness.js.map +1 -0
  495. package/dist/graph/unit-state-facts.d.ts +43 -0
  496. package/dist/graph/unit-state-facts.d.ts.map +1 -0
  497. package/dist/graph/unit-state-facts.js +87 -0
  498. package/dist/graph/unit-state-facts.js.map +1 -0
  499. package/dist/harness/audit.d.ts +14 -2
  500. package/dist/harness/audit.d.ts.map +1 -1
  501. package/dist/harness/audit.js +20 -21
  502. package/dist/harness/audit.js.map +1 -1
  503. package/dist/harness/catalog/gate-rules.yaml +647 -0
  504. package/dist/harness/defect-draft.d.ts.map +1 -1
  505. package/dist/harness/defect-draft.js +12 -2
  506. package/dist/harness/defect-draft.js.map +1 -1
  507. package/dist/harness/defects.d.ts +14 -0
  508. package/dist/harness/defects.d.ts.map +1 -1
  509. package/dist/harness/defects.js +14 -10
  510. package/dist/harness/defects.js.map +1 -1
  511. package/dist/harness/flow-check.d.ts +9 -0
  512. package/dist/harness/flow-check.d.ts.map +1 -1
  513. package/dist/harness/flow-check.js +11 -0
  514. package/dist/harness/flow-check.js.map +1 -1
  515. package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
  516. package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
  517. package/dist/harness/gates/audit-gate-terms.js +72 -0
  518. package/dist/harness/gates/audit-gate-terms.js.map +1 -0
  519. package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
  520. package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
  521. package/dist/harness/gates/audit-gate-verdict.js +102 -0
  522. package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
  523. package/dist/harness/gates/audit-input-hash.d.ts +6 -0
  524. package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
  525. package/dist/harness/gates/audit-input-hash.js +124 -0
  526. package/dist/harness/gates/audit-input-hash.js.map +1 -0
  527. package/dist/harness/gates/audit-report-verification.d.ts +43 -0
  528. package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
  529. package/dist/harness/gates/audit-report-verification.js +241 -0
  530. package/dist/harness/gates/audit-report-verification.js.map +1 -0
  531. package/dist/harness/gates/decisions-hash.d.ts +13 -0
  532. package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
  533. package/dist/harness/gates/decisions-hash.js +72 -0
  534. package/dist/harness/gates/decisions-hash.js.map +1 -0
  535. package/dist/harness/gates/delivery-checklist.d.ts +24 -0
  536. package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
  537. package/dist/harness/gates/delivery-checklist.js +161 -0
  538. package/dist/harness/gates/delivery-checklist.js.map +1 -0
  539. package/dist/harness/gates/delivery-decision-gate.d.ts +33 -0
  540. package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
  541. package/dist/harness/gates/delivery-decision-gate.js +116 -0
  542. package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
  543. package/dist/harness/gates/delivery-decision-step.d.ts +19 -0
  544. package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
  545. package/dist/harness/gates/delivery-decision-step.js +86 -0
  546. package/dist/harness/gates/delivery-decision-step.js.map +1 -0
  547. package/dist/harness/gates/gate-override-report.d.ts +59 -0
  548. package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
  549. package/dist/harness/gates/gate-override-report.js +86 -0
  550. package/dist/harness/gates/gate-override-report.js.map +1 -0
  551. package/dist/harness/gates/gate-override-scope.d.ts +18 -0
  552. package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
  553. package/dist/harness/gates/gate-override-scope.js +98 -0
  554. package/dist/harness/gates/gate-override-scope.js.map +1 -0
  555. package/dist/harness/gates/gate-override-validation.d.ts +25 -0
  556. package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
  557. package/dist/harness/gates/gate-override-validation.js +81 -0
  558. package/dist/harness/gates/gate-override-validation.js.map +1 -0
  559. package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
  560. package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
  561. package/dist/harness/gates/gate-rule-catalog.js +112 -0
  562. package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
  563. package/dist/harness/gates/gate-terms.d.ts +33 -0
  564. package/dist/harness/gates/gate-terms.d.ts.map +1 -0
  565. package/dist/harness/gates/gate-terms.js +41 -0
  566. package/dist/harness/gates/gate-terms.js.map +1 -0
  567. package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
  568. package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
  569. package/dist/harness/gates/in-run-decision-check.js +166 -0
  570. package/dist/harness/gates/in-run-decision-check.js.map +1 -0
  571. package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
  572. package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
  573. package/dist/harness/gates/journey-gate-inputs.js +81 -0
  574. package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
  575. package/dist/harness/gates/migrated-waiver-import-bounds.d.ts +13 -0
  576. package/dist/harness/gates/migrated-waiver-import-bounds.d.ts.map +1 -0
  577. package/dist/harness/gates/migrated-waiver-import-bounds.js +141 -0
  578. package/dist/harness/gates/migrated-waiver-import-bounds.js.map +1 -0
  579. package/dist/harness/gates/migrated-waiver-review.d.ts +16 -0
  580. package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
  581. package/dist/harness/gates/migrated-waiver-review.js +114 -0
  582. package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
  583. package/dist/harness/gates/resolve-rule.d.ts +59 -0
  584. package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
  585. package/dist/harness/gates/resolve-rule.js +41 -0
  586. package/dist/harness/gates/resolve-rule.js.map +1 -0
  587. package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
  588. package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
  589. package/dist/harness/gates/unit-gate-overrides.js +122 -0
  590. package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
  591. package/dist/harness/journey.d.ts +64 -1
  592. package/dist/harness/journey.d.ts.map +1 -1
  593. package/dist/harness/journey.js +258 -34
  594. package/dist/harness/journey.js.map +1 -1
  595. package/dist/harness/manifest.d.ts +12 -0
  596. package/dist/harness/manifest.d.ts.map +1 -1
  597. package/dist/harness/manifest.js +71 -43
  598. package/dist/harness/manifest.js.map +1 -1
  599. package/dist/harness/next-step.d.ts +58 -4
  600. package/dist/harness/next-step.d.ts.map +1 -1
  601. package/dist/harness/next-step.js +55 -204
  602. package/dist/harness/next-step.js.map +1 -1
  603. package/dist/harness/parse.d.ts +7 -0
  604. package/dist/harness/parse.d.ts.map +1 -1
  605. package/dist/harness/parse.js +18 -2
  606. package/dist/harness/parse.js.map +1 -1
  607. package/dist/harness/provenance.d.ts +29 -0
  608. package/dist/harness/provenance.d.ts.map +1 -1
  609. package/dist/harness/provenance.js +109 -9
  610. package/dist/harness/provenance.js.map +1 -1
  611. package/dist/harness/script-check.d.ts.map +1 -1
  612. package/dist/harness/script-check.js +22 -5
  613. package/dist/harness/script-check.js.map +1 -1
  614. package/dist/harness/sensors.d.ts +14 -3
  615. package/dist/harness/sensors.d.ts.map +1 -1
  616. package/dist/harness/sensors.js +41 -38
  617. package/dist/harness/sensors.js.map +1 -1
  618. package/dist/harness/triage-manual-classification.d.ts +32 -0
  619. package/dist/harness/triage-manual-classification.d.ts.map +1 -0
  620. package/dist/harness/triage-manual-classification.js +56 -0
  621. package/dist/harness/triage-manual-classification.js.map +1 -0
  622. package/dist/harness/unit-paths.d.ts +8 -0
  623. package/dist/harness/unit-paths.d.ts.map +1 -1
  624. package/dist/harness/unit-paths.js +23 -1
  625. package/dist/harness/unit-paths.js.map +1 -1
  626. package/dist/hooks/bash-read-only-segment.d.ts +9 -0
  627. package/dist/hooks/bash-read-only-segment.d.ts.map +1 -0
  628. package/dist/hooks/bash-read-only-segment.js +168 -0
  629. package/dist/hooks/bash-read-only-segment.js.map +1 -0
  630. package/dist/hooks/bash-write-targets.d.ts +25 -0
  631. package/dist/hooks/bash-write-targets.d.ts.map +1 -0
  632. package/dist/hooks/bash-write-targets.js +266 -0
  633. package/dist/hooks/bash-write-targets.js.map +1 -0
  634. package/dist/hooks/claude-hook-entry.d.ts +11 -0
  635. package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
  636. package/dist/hooks/claude-hook-entry.js +137 -0
  637. package/dist/hooks/claude-hook-entry.js.map +1 -0
  638. package/dist/hooks/claude-transcript-turn.d.ts +11 -0
  639. package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
  640. package/dist/hooks/claude-transcript-turn.js +143 -0
  641. package/dist/hooks/claude-transcript-turn.js.map +1 -0
  642. package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
  643. package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
  644. package/dist/hooks/guard-delivery-contract-rule.js +152 -0
  645. package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
  646. package/dist/hooks/guard-protected-paths.d.ts +13 -0
  647. package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
  648. package/dist/hooks/guard-protected-paths.js +116 -0
  649. package/dist/hooks/guard-protected-paths.js.map +1 -0
  650. package/dist/hooks/guard-rules.d.ts +20 -0
  651. package/dist/hooks/guard-rules.d.ts.map +1 -0
  652. package/dist/hooks/guard-rules.js +130 -0
  653. package/dist/hooks/guard-rules.js.map +1 -0
  654. package/dist/hooks/guard-tty-spoof-rule.d.ts +17 -0
  655. package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
  656. package/dist/hooks/guard-tty-spoof-rule.js +22 -0
  657. package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
  658. package/dist/hooks/hand-back-core.d.ts +42 -0
  659. package/dist/hooks/hand-back-core.d.ts.map +1 -0
  660. package/dist/hooks/hand-back-core.js +128 -0
  661. package/dist/hooks/hand-back-core.js.map +1 -0
  662. package/dist/hooks/hook-cli-command.d.ts +9 -0
  663. package/dist/hooks/hook-cli-command.d.ts.map +1 -0
  664. package/dist/hooks/hook-cli-command.js +46 -0
  665. package/dist/hooks/hook-cli-command.js.map +1 -0
  666. package/dist/hooks/hook-registry.d.ts +10 -0
  667. package/dist/hooks/hook-registry.d.ts.map +1 -0
  668. package/dist/hooks/hook-registry.js +24 -0
  669. package/dist/hooks/hook-registry.js.map +1 -0
  670. package/dist/hooks/hook-result-contract.d.ts +50 -0
  671. package/dist/hooks/hook-result-contract.d.ts.map +1 -0
  672. package/dist/hooks/hook-result-contract.js +80 -0
  673. package/dist/hooks/hook-result-contract.js.map +1 -0
  674. package/dist/hooks/hook-toggles.d.ts +10 -0
  675. package/dist/hooks/hook-toggles.d.ts.map +1 -0
  676. package/dist/hooks/hook-toggles.js +107 -0
  677. package/dist/hooks/hook-toggles.js.map +1 -0
  678. package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
  679. package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
  680. package/dist/hooks/pre-tool-guard-hook.js +105 -0
  681. package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
  682. package/dist/hooks/session-start-unit-state-hook.d.ts +14 -0
  683. package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
  684. package/dist/hooks/session-start-unit-state-hook.js +60 -0
  685. package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
  686. package/dist/hooks/stop-next-step-hook.d.ts +16 -0
  687. package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
  688. package/dist/hooks/stop-next-step-hook.js +43 -0
  689. package/dist/hooks/stop-next-step-hook.js.map +1 -0
  690. package/dist/hooks/sungen-project-root.d.ts +19 -0
  691. package/dist/hooks/sungen-project-root.d.ts.map +1 -0
  692. package/dist/hooks/sungen-project-root.js +130 -0
  693. package/dist/hooks/sungen-project-root.js.map +1 -0
  694. package/dist/index.d.ts.map +1 -1
  695. package/dist/index.js +1 -2
  696. package/dist/index.js.map +1 -1
  697. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
  698. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
  699. package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
  700. package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
  701. package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
  702. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
  703. package/dist/orchestrator/ai-skills/commands.js +11 -3
  704. package/dist/orchestrator/ai-skills/commands.js.map +1 -1
  705. package/dist/orchestrator/assets-drift.d.ts +5 -0
  706. package/dist/orchestrator/assets-drift.d.ts.map +1 -1
  707. package/dist/orchestrator/assets-drift.js +12 -3
  708. package/dist/orchestrator/assets-drift.js.map +1 -1
  709. package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
  710. package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
  711. package/dist/orchestrator/claude-hooks-install.js +203 -0
  712. package/dist/orchestrator/claude-hooks-install.js.map +1 -0
  713. package/dist/orchestrator/lockstep-version.d.ts +15 -5
  714. package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
  715. package/dist/orchestrator/lockstep-version.js +30 -17
  716. package/dist/orchestrator/lockstep-version.js.map +1 -1
  717. package/dist/orchestrator/project-initializer.d.ts +6 -0
  718. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  719. package/dist/orchestrator/project-initializer.js +24 -0
  720. package/dist/orchestrator/project-initializer.js.map +1 -1
  721. package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  722. package/dist/orchestrator/templates/ai-src/commands/add-flow.md +6 -26
  723. package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  724. package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  725. package/dist/orchestrator/templates/ai-src/commands/create-test.md +17 -16
  726. package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  727. package/dist/orchestrator/templates/ai-src/commands/delivery.md +4 -0
  728. package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
  729. package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  730. package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
  731. package/dist/orchestrator/templates/ai-src/commands/review.md +4 -0
  732. package/dist/orchestrator/templates/ai-src/commands/run-test.md +6 -8
  733. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  734. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  735. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  736. package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
  737. package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
  738. package/dist/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
  739. package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +1 -2
  740. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +4 -5
  741. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +8 -24
  742. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +3 -4
  743. package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  744. package/dist/orchestrator/templates/specs-test-data.ts +4 -22
  745. package/dist/utils/safe-yaml-parse.d.ts +8 -0
  746. package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
  747. package/dist/utils/safe-yaml-parse.js +51 -0
  748. package/dist/utils/safe-yaml-parse.js.map +1 -0
  749. package/package.json +3 -3
  750. package/src/basis/anchor-grammar.ts +83 -0
  751. package/src/basis/anchor-hash.ts +27 -0
  752. package/src/basis/bm51-tables.ts +65 -0
  753. package/src/basis/extract-spec-anchors.ts +95 -0
  754. package/src/basis/migration-sentinel.ts +175 -0
  755. package/src/basis/spec-frontmatter.ts +68 -0
  756. package/src/basis/spec-sections.ts +35 -0
  757. package/src/basis/test-basis-schema.ts +81 -0
  758. package/src/capabilities/discover.ts +15 -2
  759. package/src/capabilities/registry.ts +16 -0
  760. package/src/cli/case-triage-hint.ts +31 -0
  761. package/src/cli/commands/audit.ts +1 -1
  762. package/src/cli/commands/capability.ts +1 -1
  763. package/src/cli/commands/contract-derive-command.ts +72 -0
  764. package/src/cli/commands/contract.ts +164 -0
  765. package/src/cli/commands/delivery.ts +106 -19
  766. package/src/cli/commands/gate.ts +2 -1
  767. package/src/cli/commands/generate.ts +16 -3
  768. package/src/cli/commands/journey.ts +45 -18
  769. package/src/cli/commands/next.ts +92 -11
  770. package/src/cli/commands/triage.ts +41 -3
  771. package/src/cli/commands/update.ts +41 -10
  772. package/src/cli/index.ts +24 -30
  773. package/src/cli/json-error-on-exit.ts +72 -0
  774. package/src/cli/project-sungen-invocation.ts +59 -0
  775. package/src/cli/run-command-hints.ts +47 -0
  776. package/src/cli/waiting-for-you-block.ts +27 -0
  777. package/src/contract/accept-opaque-steps.ts +114 -0
  778. package/src/contract/approve-contract.ts +101 -0
  779. package/src/contract/assertion-matcher-vocabulary.ts +93 -0
  780. package/src/contract/build-contract-verdict.ts +108 -0
  781. package/src/contract/build-trace-conformance.ts +103 -0
  782. package/src/contract/classify-contract-change.ts +92 -0
  783. package/src/contract/classify-contract-unit.ts +47 -0
  784. package/src/contract/classify-trace-conformance.ts +170 -0
  785. package/src/contract/contract-git.ts +106 -0
  786. package/src/contract/contract-hash.ts +34 -0
  787. package/src/contract/contract-report-file.ts +25 -0
  788. package/src/contract/contract-schema.ts +166 -0
  789. package/src/contract/contract-units.ts +52 -0
  790. package/src/contract/contracts-lock-file.ts +98 -0
  791. package/src/contract/data-normalize.ts +66 -0
  792. package/src/contract/derive-free-feature.ts +153 -0
  793. package/src/contract/field-semantic-redaction.ts +80 -0
  794. package/src/contract/free-feature-blocks.ts +56 -0
  795. package/src/contract/lock-secret-guard.ts +95 -0
  796. package/src/contract/lock-vs-git-head.ts +38 -0
  797. package/src/contract/normalize-contract-step.ts +74 -0
  798. package/src/contract/normalize-feature-steps.ts +93 -0
  799. package/src/contract/oracle-strength.ts +80 -0
  800. package/src/contract/project-contract-blockers.ts +38 -0
  801. package/src/contract/record-contract-approval.ts +110 -0
  802. package/src/contract/render-free-gherkin.ts +118 -0
  803. package/src/contract/render-trace-sentence.ts +98 -0
  804. package/src/contract/render-trace-target.ts +68 -0
  805. package/src/contract/resolve-unit-trace-identities.ts +58 -0
  806. package/src/contract/trace/collect-trace-evidence.ts +178 -0
  807. package/src/contract/trace/identity-cache.ts +61 -0
  808. package/src/contract/trace/read-file-once.ts +95 -0
  809. package/src/contract/trace/scenario-id-claims.ts +60 -0
  810. package/src/contract/trace/select-final-attempt.ts +77 -0
  811. package/src/contract/trace/snapshot-html-renderer.ts +136 -0
  812. package/src/contract/trace/snapshot-resolver.ts +66 -0
  813. package/src/contract/trace/snapshot-store.ts +151 -0
  814. package/src/contract/trace/target-identity.ts +104 -0
  815. package/src/contract/trace/test-step-join.ts +104 -0
  816. package/src/contract/trace/trace-binding.ts +59 -0
  817. package/src/contract/trace/trace-file-reader.ts +144 -0
  818. package/src/contract/trace/trace-fingerprint.ts +68 -0
  819. package/src/contract/trace/trace-freshness.ts +110 -0
  820. package/src/contract/trace/trace-identity-resolution.ts +77 -0
  821. package/src/contract/trace/trace-run-record.ts +96 -0
  822. package/src/contract/trace/trace-steps.ts +88 -0
  823. package/src/contract/trace/trace-zip-limits.ts +77 -0
  824. package/src/contract/trace/trace-zip-reader.ts +127 -0
  825. package/src/contract/verify-approval-decisions.ts +64 -0
  826. package/src/decisions/append-decision.ts +35 -0
  827. package/src/decisions/confirmed-decision-commands.ts +149 -0
  828. package/src/decisions/decision-file.ts +93 -0
  829. package/src/decisions/decision-identity.ts +73 -0
  830. package/src/decisions/decision-integrity-check.ts +61 -0
  831. package/src/decisions/decision-schema.ts +77 -0
  832. package/src/decisions/decision-source.ts +14 -0
  833. package/src/decisions/exclusive-file-create.ts +42 -0
  834. package/src/decisions/git-head-trust.ts +76 -0
  835. package/src/decisions/human-confirmation.ts +119 -0
  836. package/src/decisions/legacy-waiver-migration.ts +146 -0
  837. package/src/decisions/read-decisions.ts +61 -0
  838. package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
  839. package/src/decisions/waiver-decisions.ts +56 -0
  840. package/src/exporters/csv-exporter.ts +12 -4
  841. package/src/exporters/matrix/build.ts +11 -9
  842. package/src/exporters/matrix/export.ts +16 -4
  843. package/src/exporters/matrix/gates.ts +62 -129
  844. package/src/exporters/matrix/render-csv.ts +3 -0
  845. package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
  846. package/src/exporters/matrix/render-xlsx.ts +3 -0
  847. package/src/exporters/matrix/types.ts +4 -1
  848. package/src/exporters/matrix/xlsx-cells.ts +1 -1
  849. package/src/exporters/test-data-resolver.ts +2 -10
  850. package/src/exporters/xlsx-exporter.ts +5 -4
  851. package/src/generators/gherkin-parser/index.ts +0 -1
  852. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +1 -2
  853. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +1 -13
  854. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -9
  855. package/src/generators/test-generator/code-generator.ts +17 -4
  856. package/src/generators/test-generator/diagnostics.ts +3 -9
  857. package/src/generators/test-generator/patterns/types.ts +0 -2
  858. package/src/generators/test-generator/step-mapper.ts +9 -56
  859. package/src/generators/test-generator/utils/data-resolver.ts +0 -53
  860. package/src/generators/test-generator/utils/selector-resolver.ts +2 -10
  861. package/src/graph/board-human-actions.ts +80 -0
  862. package/src/graph/evaluate-graph.ts +97 -0
  863. package/src/graph/human-action.ts +33 -0
  864. package/src/graph/locate-finding.ts +93 -0
  865. package/src/graph/manual-triage-decisions.ts +55 -0
  866. package/src/graph/node-decl.ts +83 -0
  867. package/src/graph/nodes/core-node-catalog.ts +15 -0
  868. package/src/graph/nodes/test-case-contract-nodes.ts +90 -0
  869. package/src/graph/nodes/test-case-nodes.ts +100 -0
  870. package/src/graph/nodes/unit-audit-nodes.ts +96 -0
  871. package/src/graph/nodes/unit-contract-node.ts +100 -0
  872. package/src/graph/nodes/unit-design-nodes.ts +89 -0
  873. package/src/graph/nodes/unit-execution-nodes.ts +77 -0
  874. package/src/graph/test-case-identity.ts +75 -0
  875. package/src/graph/test-case-results.ts +111 -0
  876. package/src/graph/test-case-satisfied.ts +58 -0
  877. package/src/graph/test-case-state-shape.ts +67 -0
  878. package/src/graph/test-case-states.ts +84 -0
  879. package/src/graph/unit-artifact-probes.ts +79 -0
  880. package/src/graph/unit-results-freshness.ts +165 -0
  881. package/src/graph/unit-state-facts.ts +112 -0
  882. package/src/harness/audit.ts +31 -18
  883. package/src/harness/catalog/gate-rules.yaml +647 -0
  884. package/src/harness/defect-draft.ts +13 -2
  885. package/src/harness/defects.ts +22 -9
  886. package/src/harness/flow-check.ts +11 -0
  887. package/src/harness/gates/audit-gate-terms.ts +80 -0
  888. package/src/harness/gates/audit-gate-verdict.ts +79 -0
  889. package/src/harness/gates/audit-input-hash.ts +79 -0
  890. package/src/harness/gates/audit-report-verification.ts +206 -0
  891. package/src/harness/gates/decisions-hash.ts +42 -0
  892. package/src/harness/gates/delivery-checklist.ts +152 -0
  893. package/src/harness/gates/delivery-decision-gate.ts +139 -0
  894. package/src/harness/gates/delivery-decision-step.ts +89 -0
  895. package/src/harness/gates/gate-override-report.ts +114 -0
  896. package/src/harness/gates/gate-override-scope.ts +66 -0
  897. package/src/harness/gates/gate-override-validation.ts +75 -0
  898. package/src/harness/gates/gate-rule-catalog.ts +82 -0
  899. package/src/harness/gates/gate-terms.ts +59 -0
  900. package/src/harness/gates/in-run-decision-check.ts +124 -0
  901. package/src/harness/gates/journey-gate-inputs.ts +89 -0
  902. package/src/harness/gates/migrated-waiver-import-bounds.ts +103 -0
  903. package/src/harness/gates/migrated-waiver-review.ts +121 -0
  904. package/src/harness/gates/resolve-rule.ts +94 -0
  905. package/src/harness/gates/unit-gate-overrides.ts +99 -0
  906. package/src/harness/journey.ts +287 -39
  907. package/src/harness/manifest.ts +67 -43
  908. package/src/harness/next-step.ts +91 -193
  909. package/src/harness/parse.ts +20 -2
  910. package/src/harness/provenance.ts +99 -11
  911. package/src/harness/script-check.ts +20 -5
  912. package/src/harness/sensors.ts +47 -40
  913. package/src/harness/triage-manual-classification.ts +76 -0
  914. package/src/harness/unit-paths.ts +19 -0
  915. package/src/hooks/bash-read-only-segment.ts +130 -0
  916. package/src/hooks/bash-write-targets.ts +195 -0
  917. package/src/hooks/claude-hook-entry.ts +85 -0
  918. package/src/hooks/claude-transcript-turn.ts +96 -0
  919. package/src/hooks/guard-delivery-contract-rule.ts +110 -0
  920. package/src/hooks/guard-protected-paths.ts +80 -0
  921. package/src/hooks/guard-rules.ts +117 -0
  922. package/src/hooks/guard-tty-spoof-rule.ts +34 -0
  923. package/src/hooks/hand-back-core.ts +118 -0
  924. package/src/hooks/hook-cli-command.ts +17 -0
  925. package/src/hooks/hook-registry.ts +24 -0
  926. package/src/hooks/hook-result-contract.ts +114 -0
  927. package/src/hooks/hook-toggles.ts +66 -0
  928. package/src/hooks/pre-tool-guard-hook.ts +67 -0
  929. package/src/hooks/session-start-unit-state-hook.ts +59 -0
  930. package/src/hooks/stop-next-step-hook.ts +38 -0
  931. package/src/hooks/sungen-project-root.ts +80 -0
  932. package/src/index.ts +1 -2
  933. package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
  934. package/src/orchestrator/ai-skills/commands.ts +17 -5
  935. package/src/orchestrator/assets-drift.ts +12 -5
  936. package/src/orchestrator/claude-hooks-install.ts +168 -0
  937. package/src/orchestrator/lockstep-version.ts +28 -15
  938. package/src/orchestrator/project-initializer.ts +22 -0
  939. package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  940. package/src/orchestrator/templates/ai-src/commands/add-flow.md +6 -26
  941. package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  942. package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  943. package/src/orchestrator/templates/ai-src/commands/create-test.md +17 -16
  944. package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  945. package/src/orchestrator/templates/ai-src/commands/delivery.md +4 -0
  946. package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
  947. package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  948. package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
  949. package/src/orchestrator/templates/ai-src/commands/review.md +4 -0
  950. package/src/orchestrator/templates/ai-src/commands/run-test.md +6 -8
  951. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  952. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  953. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  954. package/src/orchestrator/templates/ai-src/partials/waiting-for-you.claude.md +11 -0
  955. package/src/orchestrator/templates/ai-src/partials/waiting-for-you.codex.md +11 -0
  956. package/src/orchestrator/templates/ai-src/partials/waiting-for-you.copilot.md +11 -0
  957. package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +1 -2
  958. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +4 -5
  959. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +8 -24
  960. package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +3 -4
  961. package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  962. package/src/orchestrator/templates/specs-test-data.ts +4 -22
  963. package/src/utils/safe-yaml-parse.ts +44 -0
  964. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +0 -2
  965. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +0 -5
  966. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +0 -12
  967. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +0 -2
  968. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +0 -5
  969. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +0 -12
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `qa/<kind>/<unit>/contracts.lock.yaml` — read, check, write (DOS-14.3, DOS-13.8).
3
+ *
4
+ * read safe YAML (core schema, few aliases, no duplicate keys, byte cap); every entry checked
5
+ * strictly. A structural problem throws ContractLockError — a lock that cannot be read is
6
+ * not "nothing locked" (CLI exit 1, guard `ask`). An entry whose steps no longer hash to its
7
+ * `hash` was edited by hand: it is returned in `tampered` and judged as a hard finding.
8
+ * write scenario ids sorted, one block per scenario, written atomically.
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { stringify } from 'yaml';
13
+ import { writeFileAtomic } from '../harness/write-atomic';
14
+ import { safeYamlParse } from '../utils/safe-yaml-parse';
15
+ import { contractHash } from './contract-hash';
16
+ import { ContractLock, ContractScenario, ContractStep, LOCK_SCHEMA, LockEntry, lockEntryErrors } from './contract-schema';
17
+
18
+ export const LOCK_FILE_NAME = 'contracts.lock.yaml';
19
+ export const lockPathFor = (unitDir: string): string => path.join(unitDir, LOCK_FILE_NAME);
20
+
21
+ const HEADER = '# Sungen Test Contract lock (DOS-14.3) — written only by `sungen contract approve` on a terminal.\n'
22
+ + '# One block per scenario id. Commit it through review: `sungen contract check` compares it with git HEAD.\n';
23
+
24
+ export class ContractLockError extends Error {
25
+ constructor(public readonly source: string, public readonly problems: string[]) {
26
+ super(`${source} cannot be trusted: ${problems.slice(0, 5).join('; ')}${problems.length > 5 ? ` (+${problems.length - 5} more)` : ''}. Restore it from git, or delete it and approve again on a terminal.`);
27
+ this.name = 'ContractLockError';
28
+ }
29
+ }
30
+
31
+ const isObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
32
+
33
+ /**
34
+ * A lock as read. `tampered`: well-formed entries whose steps no longer hash to their `hash` — edited
35
+ * by hand. They are not a read error: they are a hard finding (`contract.lock-uncommitted`), so the
36
+ * delivery guard denies instead of asking.
37
+ */
38
+ export interface ReadLock extends ContractLock { tampered: string[] }
39
+
40
+ export function parseLockText(text: string, source: string): ReadLock {
41
+ let raw: unknown;
42
+ try { raw = safeYamlParse(text, source); } catch (e) { throw new ContractLockError(source, [e instanceof Error ? e.message : String(e)]); }
43
+ if (!isObject(raw)) throw new ContractLockError(source, ['not a mapping']);
44
+ const problems = Object.keys(raw).filter((k) => k !== 'schema' && k !== 'scenarios').map((k) => `unknown key "${k}"`);
45
+ if (raw.schema !== LOCK_SCHEMA) problems.push(`schema must be ${LOCK_SCHEMA}`);
46
+ if (!isObject(raw.scenarios)) problems.push('scenarios must be a mapping');
47
+ const scenarios = isObject(raw.scenarios) ? raw.scenarios : {};
48
+ const tampered: string[] = [];
49
+ for (const [id, entry] of Object.entries(scenarios)) {
50
+ const errors = lockEntryErrors(entry, id);
51
+ problems.push(...errors);
52
+ const e = entry as unknown as LockEntry;
53
+ if (!errors.length && contractHash(e.implementation, e.steps) !== e.hash) tampered.push(id);
54
+ }
55
+ if (problems.length) throw new ContractLockError(source, problems);
56
+ return { schema: LOCK_SCHEMA, scenarios: scenarios as Record<string, LockEntry>, tampered };
57
+ }
58
+
59
+ /** The lock at `file`, or null when there is none. Throws ContractLockError when it cannot be read as a lock. */
60
+ export function readLockFile(file: string): ReadLock | null {
61
+ let text: string;
62
+ try { text = fs.readFileSync(file, 'utf8'); } catch (e) {
63
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;
64
+ throw new ContractLockError(file, [e instanceof Error ? e.message : String(e)]);
65
+ }
66
+ return parseLockText(text, file);
67
+ }
68
+
69
+ /** The steps in their written key order, so the YAML reads the way DOS-14.1 lists the fields. */
70
+ function orderedStep(s: ContractStep): ContractStep {
71
+ const t = s.target && { ref: s.target.ref, also: s.target.also, parent: s.target.parent, nth: s.target.nth };
72
+ const o = { kind: s.kind, verb: s.verb, target: t, value: s.value, matcher: s.matcher, expected: s.expected, negated: s.negated, table: s.table };
73
+ return JSON.parse(JSON.stringify(o)) as ContractStep; // drops the undefined fields
74
+ }
75
+
76
+ export function serializeLock(lock: ContractLock): string {
77
+ const scenarios: Record<string, LockEntry> = {};
78
+ for (const id of Object.keys(lock.scenarios).sort((a, b) => a.localeCompare(b, 'en'))) {
79
+ const e = lock.scenarios[id];
80
+ scenarios[id] = {
81
+ hash: e.hash, implementation: e.implementation, approvedAt: e.approvedAt, decisionId: e.decisionId,
82
+ ...(e.humanApproved === true ? { humanApproved: true as const } : {}),
83
+ ...(e.acceptedOpaque?.length ? { acceptedOpaque: e.acceptedOpaque.map((a) => ({ step: a.step, sha: a.sha })) } : {}),
84
+ steps: e.steps.map(orderedStep),
85
+ };
86
+ }
87
+ return HEADER + stringify({ schema: LOCK_SCHEMA, scenarios }, { schema: 'core', lineWidth: 0 });
88
+ }
89
+
90
+ export function writeLockFile(file: string, lock: ContractLock): void {
91
+ fs.mkdirSync(path.dirname(file), { recursive: true });
92
+ writeFileAtomic(file, serializeLock(lock));
93
+ }
94
+
95
+ /** The entry a TTY approval writes for one scenario. Only the approve path sets `humanApproved`. */
96
+ export function lockEntryFor(sc: ContractScenario, decisionId: string, approvedAt: string): LockEntry {
97
+ return { hash: sc.hash, steps: sc.steps, implementation: sc.implementation, approvedAt, decisionId, humanApproved: true };
98
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Dynamic data in a trace → stable contract values (DOS-14.6), so two runs of the same test give
3
+ * the same fingerprint:
4
+ *
5
+ * a value equal to a test-data value → `{{key}}` (the unit's `test-data/<unit>.yaml`, flattened)
6
+ * uuid → `{{$dynamic:uuid}}`
7
+ * ISO date / date-time → `{{$dynamic:iso-date}}`
8
+ * epoch ms (13 digits) / s (10 digits) → `{{$dynamic:timestamp}}`
9
+ * any other run of 6+ digits → `{{$dynamic:number}}`
10
+ *
11
+ * Dynamic parts are replaced INSIDE a value (`qa+1792638683664@x.dev` → `qa+{{$dynamic:timestamp}}@x.dev`).
12
+ * Test-data matches only on the WHOLE value, and only for values of 3+ characters — `1` or `ok` in
13
+ * test-data must not swallow every `1` a page shows. Several keys with one value → the first by name.
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import { safeYamlParse } from '../utils/safe-yaml-parse';
18
+
19
+ export interface TestDataIndex { byValue: Map<string, string> }
20
+
21
+ const DYNAMIC: Array<[RegExp, string]> = [
22
+ [/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, 'uuid'],
23
+ [/\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?/g, 'iso-date'],
24
+ [/(?<!\d)(?:1\d{12}|1\d{9})(?!\d)/g, 'timestamp'],
25
+ [/(?<!\d)\d{6,}(?!\d)/g, 'number'],
26
+ ];
27
+
28
+ function flatten(v: unknown, prefix: string, out: Map<string, string>): void {
29
+ if (v === null || v === undefined) return;
30
+ if (typeof v === 'string' || typeof v === 'number') {
31
+ const s = String(v);
32
+ if (prefix && s.length >= 3 && !out.has(s)) out.set(s, prefix);
33
+ return;
34
+ }
35
+ if (Array.isArray(v)) { v.forEach((x, i) => flatten(x, `${prefix}.${i}`, out)); return; }
36
+ if (typeof v === 'object') {
37
+ for (const k of Object.keys(v as Record<string, unknown>).sort()) flatten((v as Record<string, unknown>)[k], prefix ? `${prefix}.${k}` : k, out);
38
+ }
39
+ }
40
+
41
+ /** The unit's base test-data file (environment overlays are not the contract's business). */
42
+ export function loadTestDataIndex(unitDir: string, unit: string): TestDataIndex {
43
+ const file = path.join(unitDir, 'test-data', `${path.basename(unit)}.yaml`);
44
+ const byValue = new Map<string, string>();
45
+ if (!fs.existsSync(file)) return { byValue };
46
+ let doc: unknown;
47
+ try { doc = safeYamlParse(fs.readFileSync(file, 'utf8')); } catch (e) {
48
+ throw new Error(`cannot read ${path.basename(file)} to normalize trace values: ${(e as Error).message.split('\n')[0]}`);
49
+ }
50
+ flatten(doc, '', byValue);
51
+ return { byValue };
52
+ }
53
+
54
+ /** Replace the dynamic parts of a value; whole-value test-data matches win. */
55
+ export function normalizeValue(value: string, index: TestDataIndex): string {
56
+ const key = index.byValue.get(value);
57
+ if (key) return `{{${key}}}`;
58
+ let out = value;
59
+ for (const [re, kind] of DYNAMIC) out = out.replace(re, `\u0000${kind}\u0001`);
60
+ return out.replace(/\u0000([a-z-]+)\u0001/g, '{{$dynamic:$1}}');
61
+ }
62
+
63
+ /** Names and texts the page shows: only the dynamic parts move (a name is never a test-data key). */
64
+ export function normalizeShownText(text: string): string {
65
+ return normalizeValue(text, { byValue: new Map() });
66
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * `sungen contract derive --screen <u>` (DOS-14.8): free tests' traces → `features/<u>.free.feature`,
3
+ * every scenario tagged `@free @proposed`, built DETERMINISTICALLY from the trace — no LLM.
4
+ *
5
+ * which tests `--grep <re>` over "<id> <title> <file>", else those whose test file is named
6
+ * after the unit (a path segment `<u>`, or a file `<u>.*` / `<u>-*`)
7
+ * which trace the final attempt, passed, bound to its run (a failed one is skipped, never derived)
8
+ * identities asked of the registered `traceIdentity` resolver (driver-ui) when one is given,
9
+ * otherwise the cache, otherwise core's heuristic — `# identity low` marks those steps
10
+ * approved a scenario QA already approved (no `@proposed`) is never rewritten
11
+ * one id, one two recorded tests claiming one id (#962) refuse the derive — both named with
12
+ * test file:line, NOTHING written for the unit: keeping one would drop the other's contract
13
+ * safety redaction by field meaning, machine paths scrubbed, then the secret scanner and the
14
+ * semantic credential check over the exact text: any hit → NOTHING is written
15
+ */
16
+ import * as fs from 'fs';
17
+ import * as os from 'os';
18
+ import * as path from 'path';
19
+ import { readCapabilities } from '../harness/capability';
20
+ import { isIdLike } from '../harness/parse';
21
+ import { MOBILE_NOT_SUPPORTED } from './build-trace-conformance';
22
+ import { loadTestDataIndex } from './data-normalize';
23
+ import { scrubKnownSecrets, scrubMachinePaths, secretScanHits, SecretHit } from './field-semantic-redaction';
24
+ import { idOfTitle, splitFeatureBlocks } from './free-feature-blocks';
25
+ import { sensitiveLiteralHits } from './lock-secret-guard';
26
+ import { scenariosOfFeatureFile } from './normalize-feature-steps';
27
+ import { featureFilesFor } from '../harness/unit-paths';
28
+ import { CONTROL_CHARS, freeFeatureText, renderTraceSteps, scenarioBlockText } from './render-free-gherkin';
29
+ import { collectRuns, TraceRun } from './trace/collect-trace-evidence';
30
+ import { idsClaimedByTwoTests } from './trace/scenario-id-claims';
31
+ import { readIdentityCache } from './trace/identity-cache';
32
+ import type { TraceIdentityResolver } from './trace/target-identity';
33
+ import { identitiesFor, resolveWithDriver } from './trace/trace-identity-resolution';
34
+ import { fingerprintOf } from './trace/trace-fingerprint';
35
+ import { traceIntentSteps } from './trace/trace-steps';
36
+
37
+ export interface DeriveInput { projectRoot: string; unit: { name: string; dir: string }; grep?: string; resolver?: TraceIdentityResolver | null }
38
+ export interface DeriveResult {
39
+ file: string;
40
+ written: boolean;
41
+ scenarios: Array<{ id: string; title: string; steps: number; low: number; opaque: number }>;
42
+ /** `approved: true` — skipped because QA already approved it (derive never rewrites one), not for want of a trace. */
43
+ skipped: Array<{ id: string; reason: string; approved?: true }>;
44
+ refused?: SecretHit[];
45
+ /**
46
+ * Ids two or more recorded tests claim (#962), each test as `file:line "title" (run record <path>)` —
47
+ * nothing was written. `staleRecords`: older test-results/ records that are a claim's only source.
48
+ */
49
+ duplicateIds?: Array<{ id: string; tests: string[]; staleRecords?: string[] }>;
50
+ note?: string;
51
+ /** A derived id another `.feature` of the unit already uses (#939) — `contract check` and approve would trip on it later. */
52
+ warnings?: string[];
53
+ }
54
+
55
+ const MACHINE_PATH = /(?:\/Users|\/home)\/[^/\s"'`]+\/|[A-Za-z]:\\Users\\/;
56
+
57
+ function belongsToUnit(testFile: string, unit: string): boolean {
58
+ const segs = testFile.toLowerCase().split(/[\\/]/);
59
+ const base = segs[segs.length - 1] ?? '';
60
+ return segs.slice(0, -1).includes(unit) || base.startsWith(`${unit}.`) || base.startsWith(`${unit}-`);
61
+ }
62
+
63
+ /** The semantic credential check over the text as it would be written. */
64
+ function semanticHits(text: string, label: string): SecretHit[] {
65
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sungen-derive-'));
66
+ try {
67
+ const file = path.join(dir, label);
68
+ fs.writeFileSync(file, text);
69
+ return sensitiveLiteralHits(scenariosOfFeatureFile(file));
70
+ } finally { fs.rmSync(dir, { recursive: true, force: true }); }
71
+ }
72
+
73
+ /**
74
+ * The derived ids another `.feature` of the unit already carries (#939): `contract check` reports an id
75
+ * used twice and approve refuses it, so say it now — when it is still one rename away — not at check.
76
+ */
77
+ function reusedIdWarnings(unit: { name: string; dir: string }, freeFile: string, ids: string[]): string[] {
78
+ const owners = new Map<string, string>();
79
+ for (const f of featureFilesFor(unit.dir, unit.name)) {
80
+ if (path.resolve(f) === path.resolve(freeFile) || !fs.existsSync(f)) continue;
81
+ try {
82
+ for (const sc of scenariosOfFeatureFile(f)) if (!owners.has(sc.id)) owners.set(sc.id, path.basename(f));
83
+ } catch { /* an unparseable sibling is `contract check`'s to report */ }
84
+ }
85
+ return ids.filter((id) => owners.has(id)).map((id) =>
86
+ `${id} is already the ID of a scenario in ${owners.get(id)} — the derived scenario reuses it; give the free test (its title's leading ID, or its @tag) or that scenario its own ID, or \`sungen contract check\` reports it twice and approval refuses it`);
87
+ }
88
+
89
+ /**
90
+ * Nothing was derived and nothing explains it away: no test of the unit left an admissible trace
91
+ * (#932). Not when the scenarios are approved (derive leaves those alone by design), up to date,
92
+ * refused, or the project is mobile (`note`).
93
+ */
94
+ export function nothingDerived(r: DeriveResult): boolean {
95
+ return !r.note && !r.refused?.length && !r.duplicateIds?.length && r.scenarios.length === 0 && !r.skipped.some((s) => s.approved);
96
+ }
97
+
98
+ export async function deriveFreeFeature(i: DeriveInput): Promise<DeriveResult> {
99
+ const unitBase = path.basename(i.unit.name);
100
+ const file = path.join(i.unit.dir, 'features', `${unitBase}.free.feature`);
101
+ const result: DeriveResult = { file, written: false, scenarios: [], skipped: [] };
102
+ if ((readCapabilities(i.projectRoot).platform ?? '').toLowerCase() === 'mobile') return { ...result, note: MOBILE_NOT_SUPPORTED };
103
+ const grep = i.grep ? new RegExp(i.grep, 'i') : null;
104
+ const found = collectRuns(i.projectRoot, (id, t) => (grep ? grep.test(`${id} ${t.title} ${t.file}`) : belongsToUnit(t.file, unitBase.toLowerCase())), null);
105
+ const claimed = idsClaimedByTwoTests(i.projectRoot, found.runs);
106
+ if (claimed.size) {
107
+ return { ...result, duplicateIds: [...claimed].sort(([a], [b]) => a.localeCompare(b, 'en')).map(([id, c]) => ({ id, tests: c.tests, ...(c.staleRecords.length ? { staleRecords: c.staleRecords } : {}) })) };
108
+ }
109
+ const byId = new Map<string, TraceRun[]>();
110
+ for (const r of found.runs) byId.set(r.scenarioId, [...(byId.get(r.scenarioId) ?? []), r]);
111
+ const admissible = (runs: TraceRun[]): TraceRun[] => runs.filter((r) => r.status === 'admissible' && r.trace)
112
+ .sort((a, b) => `${a.record?.file}|${a.test?.testId}`.localeCompare(`${b.record?.file}|${b.test?.testId}`));
113
+ if (i.resolver) await resolveWithDriver(i.projectRoot, [...byId.values()].flatMap(admissible).map((r) => r.trace!), i.resolver);
114
+
115
+ const { cache } = readIdentityCache(i.projectRoot);
116
+ const index = loadTestDataIndex(i.unit.dir, i.unit.name);
117
+ const before = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
118
+ const kept = splitFeatureBlocks(before);
119
+ const blocks = new Map(kept.map((b) => [b.id, b.text]));
120
+ for (const id of [...byId.keys()].sort((a, b) => a.localeCompare(b, 'en'))) {
121
+ const runs = admissible(byId.get(id)!);
122
+ if (!runs.length) { result.skipped.push({ id, reason: byId.get(id)![0].reason ?? 'no admissible trace' }); continue; }
123
+ if (kept.some((b) => b.id === id && !b.proposed)) { result.skipped.push({ id, reason: 'approved — derive never rewrites an approved scenario; use `sungen contract check --trace`', approved: true }); continue; }
124
+ const secrets = new Set<string>();
125
+ const rendered = runs.map((r) => {
126
+ const steps = traceIntentSteps(r.trace!);
127
+ return renderTraceSteps(steps, identitiesFor(r.trace!, steps, cache), { index, projectRoot: i.projectRoot, specFile: r.specFile, secrets });
128
+ });
129
+ const run = runs[0];
130
+ const rawTitle = run.test!.title;
131
+ const title = scrubMachinePaths(scrubKnownSecrets(idOfTitle(rawTitle) === id ? rawTitle : `${id} ${rawTitle}`, secrets), i.projectRoot).replace(CONTROL_CHARS, '');
132
+ const variants = new Set(rendered.map((r) => fingerprintOf(r, 'free').hash)).size;
133
+ const comments = [
134
+ `source: ${run.specFile}:${run.test!.line} · run ${run.record!.runId}${run.result!.retry ? ` (retry ${run.result!.retry})` : ''}`,
135
+ ...(variants > 1 ? [`nondeterministic: ${variants} runs fingerprint differently — derived from the first`] : []),
136
+ ...(isIdLike(id) ? [] : ['test outside the spec: no VP-id (spec missing, or the test is surplus)']),
137
+ ];
138
+ blocks.set(id, scenarioBlockText({ id, title, tags: ['@free', '@proposed'], comments, steps: rendered[0] }));
139
+ result.scenarios.push({ id, title, steps: rendered[0].length, low: rendered[0].filter((s) => s.low).length, opaque: rendered[0].filter((s) => s.opaque).length });
140
+ }
141
+ if (!result.scenarios.length) return result;
142
+ const reused = reusedIdWarnings(i.unit, file, result.scenarios.map((x) => x.id));
143
+ if (reused.length) result.warnings = reused;
144
+
145
+ const text = freeFeatureText(unitBase, [...blocks.values()]);
146
+ const label = path.basename(file);
147
+ const hits = [...(MACHINE_PATH.test(text) ? [{ where: label, reason: 'a machine path survived scrubbing' }] : []), ...secretScanHits(text, label), ...semanticHits(text, label)];
148
+ if (hits.length) return { ...result, refused: hits };
149
+ if (text === before) return result;
150
+ fs.mkdirSync(path.dirname(file), { recursive: true });
151
+ fs.writeFileSync(file, text);
152
+ return { ...result, written: true };
153
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Nothing secret, nothing machine-specific leaves a trace into a file sungen writes (DOS-14.7, G4).
3
+ * A login trace holds the password 13–14 times (step title, call params, snapshot value, request
4
+ * body) and up to 19 `/Users/…` paths (phase 07, R7).
5
+ *
6
+ * by field meaning a value typed into a field that IS a credential — `type=password`, or a name /
7
+ * id / autocomplete / label / accessible name that reads as one (password, token,
8
+ * secret, otp, api key…, whole words) — becomes `{{$redacted}}` (a test-data key
9
+ * wins when the value is in test-data: `{{login.password}}` is a reference)
10
+ * everywhere else every value redacted that way is also scrubbed from ALL other text written
11
+ * (expected texts, opaque verbatims, titles)
12
+ * machine paths the project root becomes relative; any other home directory becomes `~`
13
+ * last line the existing secret scanner runs over the exact text about to be written; a
14
+ * hit refuses the write — nothing is written, the place is named
15
+ */
16
+ import * as fs from 'fs';
17
+ import * as os from 'os';
18
+ import * as path from 'path';
19
+ import { scanTestDataSecrets } from '../harness/secret-scan';
20
+ import { isSensitiveFieldName } from './lock-secret-guard';
21
+ import type { TargetFacts } from './trace/target-identity';
22
+
23
+ export const REDACTED = '{{$redacted}}';
24
+
25
+ const CREDENTIAL_AUTOCOMPLETE = /(?:^|\s)(?:current-password|new-password|one-time-code|cc-number|cc-csc)(?:\s|$)/i;
26
+
27
+ /** Is the element a value was typed into a credential field? */
28
+ export function isSensitiveTarget(facts: TargetFacts | null | undefined, accessibleName?: string): boolean {
29
+ const a = facts?.attrs ?? {};
30
+ if ((a.type ?? '').toLowerCase() === 'password') return true;
31
+ if (CREDENTIAL_AUTOCOMPLETE.test(a.autocomplete ?? '')) return true;
32
+ return [a.name, a.id, a.autocomplete, a['aria-label'], a.placeholder, facts?.labelText, accessibleName].some((n) => isSensitiveFieldName(n));
33
+ }
34
+
35
+ /** Replace every occurrence of a known secret value in `text`. Values shorter than 4 chars are left (noise). */
36
+ export function scrubKnownSecrets(text: string, secrets: Iterable<string>): string {
37
+ let out = text;
38
+ for (const s of [...new Set(secrets)].filter((x) => x.length >= 4).sort((a, b) => b.length - a.length)) out = out.split(s).join(REDACTED);
39
+ return out;
40
+ }
41
+
42
+ /** Project-relative paths; other home directories → `~` (`/Users/<name>`, `/home/<name>`, `C:\Users\<name>`). */
43
+ export function scrubMachinePaths(text: string, projectRoot: string): string {
44
+ const root = path.resolve(projectRoot);
45
+ return text
46
+ .split(root + path.sep).join('')
47
+ .split(root).join('.')
48
+ .replace(/(?:\/Users|\/home)\/[^/\s"'`]+/g, '~')
49
+ .replace(/[A-Za-z]:\\Users\\[^\\\s"'`]+/g, '~');
50
+ }
51
+
52
+ /** A path for display: project-relative when inside the project, the basename otherwise. */
53
+ export function displayPath(file: string, projectRoot: string): string {
54
+ const rel = path.relative(path.resolve(projectRoot), path.resolve(projectRoot, file));
55
+ return rel.startsWith('..') || path.isAbsolute(rel) ? path.basename(file) : rel.split(path.sep).join('/');
56
+ }
57
+
58
+ export interface SecretHit { where: string; reason: string }
59
+
60
+ /** The secret scanner over the exact text about to be written to `label`. */
61
+ export function secretScanHits(text: string, label: string): SecretHit[] {
62
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sungen-trace-scan-'));
63
+ try {
64
+ fs.mkdirSync(path.join(dir, 'test-data'));
65
+ fs.writeFileSync(path.join(dir, 'test-data', 'scan.yaml'), text);
66
+ const result = scanTestDataSecrets(dir);
67
+ if (result.unscanned.length) throw new Error(`the secret scan could not read the text it was given (${result.unscanned[0].reason})`);
68
+ return result.hits.map((h) => ({ where: `${label}:${h.line}`, reason: h.reason }));
69
+ } finally {
70
+ fs.rmSync(dir, { recursive: true, force: true });
71
+ }
72
+ }
73
+
74
+ export class TraceSecretError extends Error {
75
+ constructor(public readonly hits: SecretHit[]) {
76
+ super(`Refused: ${hits.length} likely secret(s) would be written. Nothing was written.\n${hits.map((h) => ` ${h.where} — ${h.reason}`).join('\n')}`
77
+ + '\n Move the value into test-data (or an env overlay) and use it from there, then derive again.');
78
+ this.name = 'TraceSecretError';
79
+ }
80
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Scenario blocks of a derived `features/<unit>.free.feature` (DOS-14.8) — so `contract derive`
3
+ * can replace what is still `@proposed` while leaving every APPROVED scenario exactly as QA approved
4
+ * it, and `contract approve` can drop the `@proposed` tag of what it just locked.
5
+ *
6
+ * A block is a scenario's tag/comment lines directly above its `Scenario:` line, through the line
7
+ * before the next block. Its id is the title's leading id (`VP-LOGIN-001`), else the title — the
8
+ * same rule the contract uses.
9
+ */
10
+ import * as fs from 'fs';
11
+ import { isIdLike } from '../harness/parse';
12
+
13
+ export interface FeatureBlock { id: string; proposed: boolean; text: string }
14
+
15
+ const SCENARIO = /^\s*Scenario(?: Outline| Template)?:\s*(.*)$/;
16
+ const TAG_OR_COMMENT = /^\s*(?:@|#)/;
17
+
18
+ export function idOfTitle(title: string): string {
19
+ const lead = title.match(/^\s*([A-Za-z][A-Za-z0-9.-]*)/)?.[1];
20
+ return lead && isIdLike(lead) ? lead.toUpperCase() : title.replace(/\s+/g, ' ').trim();
21
+ }
22
+
23
+ export function splitFeatureBlocks(text: string): FeatureBlock[] {
24
+ const lines = text.split(/\r?\n/);
25
+ const featureAt = lines.findIndex((l) => /^\s*Feature:/.test(l));
26
+ const heads = lines.map((l, i) => (SCENARIO.test(l) && i > featureAt ? i : -1)).filter((i) => i >= 0);
27
+ const starts = heads.map((h, k) => {
28
+ let s = h;
29
+ const floor = k > 0 ? heads[k - 1] + 1 : featureAt + 1;
30
+ while (s - 1 >= floor && TAG_OR_COMMENT.test(lines[s - 1])) s--;
31
+ return s;
32
+ });
33
+ return heads.map((h, k) => {
34
+ const end = k + 1 < starts.length ? starts[k + 1] : lines.length;
35
+ const body = lines.slice(starts[k], end);
36
+ while (body.length && !body[body.length - 1].trim()) body.pop();
37
+ const tags = lines.slice(starts[k], h).filter((l) => /^\s*@/.test(l)).join(' ');
38
+ return { id: idOfTitle(SCENARIO.exec(lines[h])![1]), proposed: /(^|\s)@proposed(\s|$)/.test(tags), text: body.join('\n') };
39
+ });
40
+ }
41
+
42
+ /** Approve dropped the tag: remove `@proposed` from the given scenarios. Returns true when the file changed. */
43
+ export function dropProposedTags(file: string, ids: ReadonlySet<string>): boolean {
44
+ if (!fs.existsSync(file)) return false;
45
+ const text = fs.readFileSync(file, 'utf8');
46
+ let changed = false;
47
+ let out = text;
48
+ for (const b of splitFeatureBlocks(text)) {
49
+ if (!b.proposed || !ids.has(b.id)) continue;
50
+ const next = b.text.split('\n').map((l) => (/^\s*@/.test(l) ? l.replace(/\s*@proposed(?=\s|$)/g, '') : l)).filter((l) => l.trim() !== '').join('\n');
51
+ out = out.replace(b.text, () => next);
52
+ changed = true;
53
+ }
54
+ if (changed) fs.writeFileSync(file, out);
55
+ return changed;
56
+ }
@@ -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
+ }