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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (866) hide show
  1. package/dist/basis/anchor-grammar.d.ts +32 -0
  2. package/dist/basis/anchor-grammar.d.ts.map +1 -0
  3. package/dist/basis/anchor-grammar.js +82 -0
  4. package/dist/basis/anchor-grammar.js.map +1 -0
  5. package/dist/basis/anchor-hash.d.ts +6 -0
  6. package/dist/basis/anchor-hash.d.ts.map +1 -0
  7. package/dist/basis/anchor-hash.js +28 -0
  8. package/dist/basis/anchor-hash.js.map +1 -0
  9. package/dist/basis/bm51-tables.d.ts +20 -0
  10. package/dist/basis/bm51-tables.d.ts.map +1 -0
  11. package/dist/basis/bm51-tables.js +71 -0
  12. package/dist/basis/bm51-tables.js.map +1 -0
  13. package/dist/basis/extract-spec-anchors.d.ts +39 -0
  14. package/dist/basis/extract-spec-anchors.d.ts.map +1 -0
  15. package/dist/basis/extract-spec-anchors.js +81 -0
  16. package/dist/basis/extract-spec-anchors.js.map +1 -0
  17. package/dist/basis/migration-sentinel.d.ts +47 -0
  18. package/dist/basis/migration-sentinel.d.ts.map +1 -0
  19. package/dist/basis/migration-sentinel.js +219 -0
  20. package/dist/basis/migration-sentinel.js.map +1 -0
  21. package/dist/basis/spec-frontmatter.d.ts +18 -0
  22. package/dist/basis/spec-frontmatter.d.ts.map +1 -0
  23. package/dist/basis/spec-frontmatter.js +55 -0
  24. package/dist/basis/spec-frontmatter.js.map +1 -0
  25. package/dist/basis/spec-sections.d.ts +18 -0
  26. package/dist/basis/spec-sections.d.ts.map +1 -0
  27. package/dist/basis/spec-sections.js +27 -0
  28. package/dist/basis/spec-sections.js.map +1 -0
  29. package/dist/basis/test-basis-schema.d.ts +28 -0
  30. package/dist/basis/test-basis-schema.d.ts.map +1 -0
  31. package/dist/basis/test-basis-schema.js +141 -0
  32. package/dist/basis/test-basis-schema.js.map +1 -0
  33. package/dist/capabilities/registry.d.ts +15 -0
  34. package/dist/capabilities/registry.d.ts.map +1 -1
  35. package/dist/capabilities/registry.js.map +1 -1
  36. package/dist/cli/commands/audit.d.ts.map +1 -1
  37. package/dist/cli/commands/audit.js +9 -4
  38. package/dist/cli/commands/audit.js.map +1 -1
  39. package/dist/cli/commands/capability.d.ts.map +1 -1
  40. package/dist/cli/commands/capability.js +6 -2
  41. package/dist/cli/commands/capability.js.map +1 -1
  42. package/dist/cli/commands/contract-derive-command.d.ts +10 -0
  43. package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
  44. package/dist/cli/commands/contract-derive-command.js +99 -0
  45. package/dist/cli/commands/contract-derive-command.js.map +1 -0
  46. package/dist/cli/commands/contract.d.ts +17 -0
  47. package/dist/cli/commands/contract.d.ts.map +1 -0
  48. package/dist/cli/commands/contract.js +162 -0
  49. package/dist/cli/commands/contract.js.map +1 -0
  50. package/dist/cli/commands/defect.d.ts +7 -0
  51. package/dist/cli/commands/defect.d.ts.map +1 -0
  52. package/dist/cli/commands/defect.js +68 -0
  53. package/dist/cli/commands/defect.js.map +1 -0
  54. package/dist/cli/commands/delivery.d.ts.map +1 -1
  55. package/dist/cli/commands/delivery.js +87 -10
  56. package/dist/cli/commands/delivery.js.map +1 -1
  57. package/dist/cli/commands/generate.d.ts.map +1 -1
  58. package/dist/cli/commands/generate.js +3 -1
  59. package/dist/cli/commands/generate.js.map +1 -1
  60. package/dist/cli/commands/journey.d.ts.map +1 -1
  61. package/dist/cli/commands/journey.js +41 -13
  62. package/dist/cli/commands/journey.js.map +1 -1
  63. package/dist/cli/commands/next.d.ts +8 -0
  64. package/dist/cli/commands/next.d.ts.map +1 -1
  65. package/dist/cli/commands/next.js +64 -6
  66. package/dist/cli/commands/next.js.map +1 -1
  67. package/dist/cli/commands/triage.d.ts +7 -0
  68. package/dist/cli/commands/triage.d.ts.map +1 -0
  69. package/dist/cli/commands/triage.js +98 -0
  70. package/dist/cli/commands/triage.js.map +1 -0
  71. package/dist/cli/commands/update.d.ts +11 -0
  72. package/dist/cli/commands/update.d.ts.map +1 -1
  73. package/dist/cli/commands/update.js +31 -6
  74. package/dist/cli/commands/update.js.map +1 -1
  75. package/dist/cli/index.js +24 -33
  76. package/dist/cli/index.js.map +1 -1
  77. package/dist/contract/approve-contract.d.ts +31 -0
  78. package/dist/contract/approve-contract.d.ts.map +1 -0
  79. package/dist/contract/approve-contract.js +87 -0
  80. package/dist/contract/approve-contract.js.map +1 -0
  81. package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
  82. package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
  83. package/dist/contract/assertion-matcher-vocabulary.js +100 -0
  84. package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
  85. package/dist/contract/build-contract-verdict.d.ts +32 -0
  86. package/dist/contract/build-contract-verdict.d.ts.map +1 -0
  87. package/dist/contract/build-contract-verdict.js +123 -0
  88. package/dist/contract/build-contract-verdict.js.map +1 -0
  89. package/dist/contract/build-trace-conformance.d.ts +18 -0
  90. package/dist/contract/build-trace-conformance.d.ts.map +1 -0
  91. package/dist/contract/build-trace-conformance.js +153 -0
  92. package/dist/contract/build-trace-conformance.js.map +1 -0
  93. package/dist/contract/classify-contract-change.d.ts +23 -0
  94. package/dist/contract/classify-contract-change.d.ts.map +1 -0
  95. package/dist/contract/classify-contract-change.js +98 -0
  96. package/dist/contract/classify-contract-change.js.map +1 -0
  97. package/dist/contract/classify-contract-unit.d.ts +18 -0
  98. package/dist/contract/classify-contract-unit.d.ts.map +1 -0
  99. package/dist/contract/classify-contract-unit.js +49 -0
  100. package/dist/contract/classify-contract-unit.js.map +1 -0
  101. package/dist/contract/classify-trace-conformance.d.ts +32 -0
  102. package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
  103. package/dist/contract/classify-trace-conformance.js +117 -0
  104. package/dist/contract/classify-trace-conformance.js.map +1 -0
  105. package/dist/contract/contract-git.d.ts +24 -0
  106. package/dist/contract/contract-git.d.ts.map +1 -0
  107. package/dist/contract/contract-git.js +144 -0
  108. package/dist/contract/contract-git.js.map +1 -0
  109. package/dist/contract/contract-hash.d.ts +7 -0
  110. package/dist/contract/contract-hash.d.ts.map +1 -0
  111. package/dist/contract/contract-hash.js +38 -0
  112. package/dist/contract/contract-hash.js.map +1 -0
  113. package/dist/contract/contract-report-file.d.ts +4 -0
  114. package/dist/contract/contract-report-file.d.ts.map +1 -0
  115. package/dist/contract/contract-report-file.js +60 -0
  116. package/dist/contract/contract-report-file.js.map +1 -0
  117. package/dist/contract/contract-schema.d.ts +71 -0
  118. package/dist/contract/contract-schema.d.ts.map +1 -0
  119. package/dist/contract/contract-schema.js +102 -0
  120. package/dist/contract/contract-schema.js.map +1 -0
  121. package/dist/contract/contract-units.d.ts +12 -0
  122. package/dist/contract/contract-units.d.ts.map +1 -0
  123. package/dist/contract/contract-units.js +96 -0
  124. package/dist/contract/contract-units.js.map +1 -0
  125. package/dist/contract/contracts-lock-file.d.ts +24 -0
  126. package/dist/contract/contracts-lock-file.d.ts.map +1 -0
  127. package/dist/contract/contracts-lock-file.js +140 -0
  128. package/dist/contract/contracts-lock-file.js.map +1 -0
  129. package/dist/contract/data-normalize.d.ts +10 -0
  130. package/dist/contract/data-normalize.d.ts.map +1 -0
  131. package/dist/contract/data-normalize.js +110 -0
  132. package/dist/contract/data-normalize.js.map +1 -0
  133. package/dist/contract/derive-free-feature.d.ts +30 -0
  134. package/dist/contract/derive-free-feature.d.ts.map +1 -0
  135. package/dist/contract/derive-free-feature.js +145 -0
  136. package/dist/contract/derive-free-feature.js.map +1 -0
  137. package/dist/contract/field-semantic-redaction.d.ts +21 -0
  138. package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
  139. package/dist/contract/field-semantic-redaction.js +118 -0
  140. package/dist/contract/field-semantic-redaction.js.map +1 -0
  141. package/dist/contract/free-feature-blocks.d.ts +10 -0
  142. package/dist/contract/free-feature-blocks.d.ts.map +1 -0
  143. package/dist/contract/free-feature-blocks.js +94 -0
  144. package/dist/contract/free-feature-blocks.js.map +1 -0
  145. package/dist/contract/lock-secret-guard.d.ts +22 -0
  146. package/dist/contract/lock-secret-guard.d.ts.map +1 -0
  147. package/dist/contract/lock-secret-guard.js +134 -0
  148. package/dist/contract/lock-secret-guard.js.map +1 -0
  149. package/dist/contract/lock-vs-git-head.d.ts +26 -0
  150. package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
  151. package/dist/contract/lock-vs-git-head.js +28 -0
  152. package/dist/contract/lock-vs-git-head.js.map +1 -0
  153. package/dist/contract/normalize-contract-step.d.ts +21 -0
  154. package/dist/contract/normalize-contract-step.d.ts.map +1 -0
  155. package/dist/contract/normalize-contract-step.js +70 -0
  156. package/dist/contract/normalize-contract-step.js.map +1 -0
  157. package/dist/contract/normalize-feature-steps.d.ts +7 -0
  158. package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
  159. package/dist/contract/normalize-feature-steps.js +127 -0
  160. package/dist/contract/normalize-feature-steps.js.map +1 -0
  161. package/dist/contract/oracle-strength.d.ts +12 -0
  162. package/dist/contract/oracle-strength.d.ts.map +1 -0
  163. package/dist/contract/oracle-strength.js +88 -0
  164. package/dist/contract/oracle-strength.js.map +1 -0
  165. package/dist/contract/project-contract-blockers.d.ts +10 -0
  166. package/dist/contract/project-contract-blockers.d.ts.map +1 -0
  167. package/dist/contract/project-contract-blockers.js +73 -0
  168. package/dist/contract/project-contract-blockers.js.map +1 -0
  169. package/dist/contract/record-contract-approval.d.ts +21 -0
  170. package/dist/contract/record-contract-approval.d.ts.map +1 -0
  171. package/dist/contract/record-contract-approval.js +98 -0
  172. package/dist/contract/record-contract-approval.js.map +1 -0
  173. package/dist/contract/render-free-gherkin.d.ts +40 -0
  174. package/dist/contract/render-free-gherkin.d.ts.map +1 -0
  175. package/dist/contract/render-free-gherkin.js +141 -0
  176. package/dist/contract/render-free-gherkin.js.map +1 -0
  177. package/dist/contract/render-trace-sentence.d.ts +20 -0
  178. package/dist/contract/render-trace-sentence.d.ts.map +1 -0
  179. package/dist/contract/render-trace-sentence.js +109 -0
  180. package/dist/contract/render-trace-sentence.js.map +1 -0
  181. package/dist/contract/render-trace-target.d.ts +14 -0
  182. package/dist/contract/render-trace-target.d.ts.map +1 -0
  183. package/dist/contract/render-trace-target.js +70 -0
  184. package/dist/contract/render-trace-target.js.map +1 -0
  185. package/dist/contract/resolve-unit-trace-identities.d.ts +8 -0
  186. package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
  187. package/dist/contract/resolve-unit-trace-identities.js +78 -0
  188. package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
  189. package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
  190. package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
  191. package/dist/contract/trace/collect-trace-evidence.js +161 -0
  192. package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
  193. package/dist/contract/trace/identity-cache.d.ts +19 -0
  194. package/dist/contract/trace/identity-cache.d.ts.map +1 -0
  195. package/dist/contract/trace/identity-cache.js +104 -0
  196. package/dist/contract/trace/identity-cache.js.map +1 -0
  197. package/dist/contract/trace/select-final-attempt.d.ts +27 -0
  198. package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
  199. package/dist/contract/trace/select-final-attempt.js +116 -0
  200. package/dist/contract/trace/select-final-attempt.js.map +1 -0
  201. package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
  202. package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
  203. package/dist/contract/trace/snapshot-html-renderer.js +172 -0
  204. package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
  205. package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
  206. package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
  207. package/dist/contract/trace/snapshot-resolver.js +65 -0
  208. package/dist/contract/trace/snapshot-resolver.js.map +1 -0
  209. package/dist/contract/trace/snapshot-store.d.ts +66 -0
  210. package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
  211. package/dist/contract/trace/snapshot-store.js +173 -0
  212. package/dist/contract/trace/snapshot-store.js.map +1 -0
  213. package/dist/contract/trace/target-identity.d.ts +74 -0
  214. package/dist/contract/trace/target-identity.d.ts.map +1 -0
  215. package/dist/contract/trace/target-identity.js +68 -0
  216. package/dist/contract/trace/target-identity.js.map +1 -0
  217. package/dist/contract/trace/test-step-join.d.ts +65 -0
  218. package/dist/contract/trace/test-step-join.d.ts.map +1 -0
  219. package/dist/contract/trace/test-step-join.js +80 -0
  220. package/dist/contract/trace/test-step-join.js.map +1 -0
  221. package/dist/contract/trace/trace-binding.d.ts +21 -0
  222. package/dist/contract/trace/trace-binding.d.ts.map +1 -0
  223. package/dist/contract/trace/trace-binding.js +88 -0
  224. package/dist/contract/trace/trace-binding.js.map +1 -0
  225. package/dist/contract/trace/trace-file-reader.d.ts +35 -0
  226. package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
  227. package/dist/contract/trace/trace-file-reader.js +155 -0
  228. package/dist/contract/trace/trace-file-reader.js.map +1 -0
  229. package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
  230. package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
  231. package/dist/contract/trace/trace-fingerprint.js +96 -0
  232. package/dist/contract/trace/trace-fingerprint.js.map +1 -0
  233. package/dist/contract/trace/trace-identity-resolution.d.ts +26 -0
  234. package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
  235. package/dist/contract/trace/trace-identity-resolution.js +84 -0
  236. package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
  237. package/dist/contract/trace/trace-run-record.d.ts +27 -0
  238. package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
  239. package/dist/contract/trace/trace-run-record.js +115 -0
  240. package/dist/contract/trace/trace-run-record.js.map +1 -0
  241. package/dist/contract/trace/trace-steps.d.ts +19 -0
  242. package/dist/contract/trace/trace-steps.d.ts.map +1 -0
  243. package/dist/contract/trace/trace-steps.js +79 -0
  244. package/dist/contract/trace/trace-steps.js.map +1 -0
  245. package/dist/contract/trace/trace-zip-limits.d.ts +41 -0
  246. package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
  247. package/dist/contract/trace/trace-zip-limits.js +48 -0
  248. package/dist/contract/trace/trace-zip-limits.js.map +1 -0
  249. package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
  250. package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
  251. package/dist/contract/trace/trace-zip-reader.js +186 -0
  252. package/dist/contract/trace/trace-zip-reader.js.map +1 -0
  253. package/dist/contract/verify-approval-decisions.d.ts +5 -0
  254. package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
  255. package/dist/contract/verify-approval-decisions.js +104 -0
  256. package/dist/contract/verify-approval-decisions.js.map +1 -0
  257. package/dist/decisions/append-decision.d.ts +12 -0
  258. package/dist/decisions/append-decision.d.ts.map +1 -0
  259. package/dist/decisions/append-decision.js +73 -0
  260. package/dist/decisions/append-decision.js.map +1 -0
  261. package/dist/decisions/confirmed-decision-commands.d.ts +30 -0
  262. package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
  263. package/dist/decisions/confirmed-decision-commands.js +172 -0
  264. package/dist/decisions/confirmed-decision-commands.js.map +1 -0
  265. package/dist/decisions/decision-file.d.ts +31 -0
  266. package/dist/decisions/decision-file.d.ts.map +1 -0
  267. package/dist/decisions/decision-file.js +139 -0
  268. package/dist/decisions/decision-file.js.map +1 -0
  269. package/dist/decisions/decision-identity.d.ts +12 -0
  270. package/dist/decisions/decision-identity.d.ts.map +1 -0
  271. package/dist/decisions/decision-identity.js +78 -0
  272. package/dist/decisions/decision-identity.js.map +1 -0
  273. package/dist/decisions/decision-integrity-check.d.ts +12 -0
  274. package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
  275. package/dist/decisions/decision-integrity-check.js +102 -0
  276. package/dist/decisions/decision-integrity-check.js.map +1 -0
  277. package/dist/decisions/decision-schema.d.ts +42 -0
  278. package/dist/decisions/decision-schema.d.ts.map +1 -0
  279. package/dist/decisions/decision-schema.js +75 -0
  280. package/dist/decisions/decision-schema.js.map +1 -0
  281. package/dist/decisions/decision-source.d.ts +2 -0
  282. package/dist/decisions/decision-source.d.ts.map +1 -0
  283. package/dist/decisions/decision-source.js +18 -0
  284. package/dist/decisions/decision-source.js.map +1 -0
  285. package/dist/decisions/exclusive-file-create.d.ts +3 -0
  286. package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
  287. package/dist/decisions/exclusive-file-create.js +84 -0
  288. package/dist/decisions/exclusive-file-create.js.map +1 -0
  289. package/dist/decisions/git-head-trust.d.ts +7 -0
  290. package/dist/decisions/git-head-trust.d.ts.map +1 -0
  291. package/dist/decisions/git-head-trust.js +76 -0
  292. package/dist/decisions/git-head-trust.js.map +1 -0
  293. package/dist/decisions/human-confirmation.d.ts +33 -0
  294. package/dist/decisions/human-confirmation.d.ts.map +1 -0
  295. package/dist/decisions/human-confirmation.js +155 -0
  296. package/dist/decisions/human-confirmation.js.map +1 -0
  297. package/dist/decisions/legacy-waiver-migration.d.ts +31 -0
  298. package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
  299. package/dist/decisions/legacy-waiver-migration.js +170 -0
  300. package/dist/decisions/legacy-waiver-migration.js.map +1 -0
  301. package/dist/decisions/read-decisions.d.ts +19 -0
  302. package/dist/decisions/read-decisions.d.ts.map +1 -0
  303. package/dist/decisions/read-decisions.js +90 -0
  304. package/dist/decisions/read-decisions.js.map +1 -0
  305. package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
  306. package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
  307. package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
  308. package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
  309. package/dist/decisions/waiver-decisions.d.ts +24 -0
  310. package/dist/decisions/waiver-decisions.d.ts.map +1 -0
  311. package/dist/decisions/waiver-decisions.js +46 -0
  312. package/dist/decisions/waiver-decisions.js.map +1 -0
  313. package/dist/exporters/csv-exporter.d.ts +4 -0
  314. package/dist/exporters/csv-exporter.d.ts.map +1 -1
  315. package/dist/exporters/csv-exporter.js +1 -1
  316. package/dist/exporters/csv-exporter.js.map +1 -1
  317. package/dist/exporters/feature-parser.d.ts +8 -0
  318. package/dist/exporters/feature-parser.d.ts.map +1 -1
  319. package/dist/exporters/feature-parser.js +12 -0
  320. package/dist/exporters/feature-parser.js.map +1 -1
  321. package/dist/exporters/matrix/build.d.ts +3 -0
  322. package/dist/exporters/matrix/build.d.ts.map +1 -1
  323. package/dist/exporters/matrix/build.js +59 -19
  324. package/dist/exporters/matrix/build.js.map +1 -1
  325. package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
  326. package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
  327. package/dist/exporters/matrix/carry-manual-results.js +76 -6
  328. package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
  329. package/dist/exporters/matrix/export.d.ts +8 -2
  330. package/dist/exporters/matrix/export.d.ts.map +1 -1
  331. package/dist/exporters/matrix/export.js +16 -3
  332. package/dist/exporters/matrix/export.js.map +1 -1
  333. package/dist/exporters/matrix/gates.d.ts +6 -1
  334. package/dist/exporters/matrix/gates.d.ts.map +1 -1
  335. package/dist/exporters/matrix/gates.js +57 -128
  336. package/dist/exporters/matrix/gates.js.map +1 -1
  337. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  338. package/dist/exporters/matrix/render-csv.js +4 -1
  339. package/dist/exporters/matrix/render-csv.js.map +1 -1
  340. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
  341. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
  342. package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
  343. package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
  344. package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
  345. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  346. package/dist/exporters/matrix/render-xlsx.js +6 -0
  347. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  348. package/dist/exporters/matrix/types.d.ts +9 -1
  349. package/dist/exporters/matrix/types.d.ts.map +1 -1
  350. package/dist/exporters/matrix/types.js.map +1 -1
  351. package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
  352. package/dist/exporters/playwright-report-parser.js +1 -0
  353. package/dist/exporters/playwright-report-parser.js.map +1 -1
  354. package/dist/exporters/scenario-merger.d.ts.map +1 -1
  355. package/dist/exporters/scenario-merger.js +2 -0
  356. package/dist/exporters/scenario-merger.js.map +1 -1
  357. package/dist/exporters/types.d.ts +5 -0
  358. package/dist/exporters/types.d.ts.map +1 -1
  359. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  360. package/dist/generators/test-generator/code-generator.js +13 -0
  361. package/dist/generators/test-generator/code-generator.js.map +1 -1
  362. package/dist/graph/evaluate-graph.d.ts +21 -0
  363. package/dist/graph/evaluate-graph.d.ts.map +1 -0
  364. package/dist/graph/evaluate-graph.js +49 -0
  365. package/dist/graph/evaluate-graph.js.map +1 -0
  366. package/dist/graph/locate-finding.d.ts +20 -0
  367. package/dist/graph/locate-finding.d.ts.map +1 -0
  368. package/dist/graph/locate-finding.js +137 -0
  369. package/dist/graph/locate-finding.js.map +1 -0
  370. package/dist/graph/node-decl.d.ts +59 -0
  371. package/dist/graph/node-decl.d.ts.map +1 -0
  372. package/dist/graph/node-decl.js +8 -0
  373. package/dist/graph/node-decl.js.map +1 -0
  374. package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
  375. package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
  376. package/dist/graph/nodes/core-node-catalog.js +13 -0
  377. package/dist/graph/nodes/core-node-catalog.js.map +1 -0
  378. package/dist/graph/nodes/test-case-contract-nodes.d.ts +19 -0
  379. package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
  380. package/dist/graph/nodes/test-case-contract-nodes.js +56 -0
  381. package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
  382. package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
  383. package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
  384. package/dist/graph/nodes/test-case-nodes.js +66 -0
  385. package/dist/graph/nodes/test-case-nodes.js.map +1 -0
  386. package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
  387. package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
  388. package/dist/graph/nodes/unit-audit-nodes.js +95 -0
  389. package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
  390. package/dist/graph/nodes/unit-contract-node.d.ts +20 -0
  391. package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
  392. package/dist/graph/nodes/unit-contract-node.js +81 -0
  393. package/dist/graph/nodes/unit-contract-node.js.map +1 -0
  394. package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
  395. package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
  396. package/dist/graph/nodes/unit-design-nodes.js +90 -0
  397. package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
  398. package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
  399. package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
  400. package/dist/graph/nodes/unit-execution-nodes.js +79 -0
  401. package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
  402. package/dist/graph/test-case-identity.d.ts +13 -0
  403. package/dist/graph/test-case-identity.d.ts.map +1 -0
  404. package/dist/graph/test-case-identity.js +106 -0
  405. package/dist/graph/test-case-identity.js.map +1 -0
  406. package/dist/graph/test-case-results.d.ts +11 -0
  407. package/dist/graph/test-case-results.d.ts.map +1 -0
  408. package/dist/graph/test-case-results.js +137 -0
  409. package/dist/graph/test-case-results.js.map +1 -0
  410. package/dist/graph/test-case-satisfied.d.ts +25 -0
  411. package/dist/graph/test-case-satisfied.d.ts.map +1 -0
  412. package/dist/graph/test-case-satisfied.js +51 -0
  413. package/dist/graph/test-case-satisfied.js.map +1 -0
  414. package/dist/graph/test-case-state-shape.d.ts +67 -0
  415. package/dist/graph/test-case-state-shape.d.ts.map +1 -0
  416. package/dist/graph/test-case-state-shape.js +3 -0
  417. package/dist/graph/test-case-state-shape.js.map +1 -0
  418. package/dist/graph/test-case-states.d.ts +9 -0
  419. package/dist/graph/test-case-states.d.ts.map +1 -0
  420. package/dist/graph/test-case-states.js +116 -0
  421. package/dist/graph/test-case-states.js.map +1 -0
  422. package/dist/graph/unit-artifact-probes.d.ts +19 -0
  423. package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
  424. package/dist/graph/unit-artifact-probes.js +133 -0
  425. package/dist/graph/unit-artifact-probes.js.map +1 -0
  426. package/dist/graph/unit-state-facts.d.ts +43 -0
  427. package/dist/graph/unit-state-facts.d.ts.map +1 -0
  428. package/dist/graph/unit-state-facts.js +87 -0
  429. package/dist/graph/unit-state-facts.js.map +1 -0
  430. package/dist/harness/audit.d.ts +11 -0
  431. package/dist/harness/audit.d.ts.map +1 -1
  432. package/dist/harness/audit.js +34 -9
  433. package/dist/harness/audit.js.map +1 -1
  434. package/dist/harness/catalog/gate-rules.yaml +647 -0
  435. package/dist/harness/counted-assertions.d.ts +2 -0
  436. package/dist/harness/counted-assertions.d.ts.map +1 -0
  437. package/dist/harness/counted-assertions.js +38 -0
  438. package/dist/harness/counted-assertions.js.map +1 -0
  439. package/dist/harness/defect-draft.d.ts +14 -0
  440. package/dist/harness/defect-draft.d.ts.map +1 -0
  441. package/dist/harness/defect-draft.js +143 -0
  442. package/dist/harness/defect-draft.js.map +1 -0
  443. package/dist/harness/defects.d.ts +24 -0
  444. package/dist/harness/defects.d.ts.map +1 -0
  445. package/dist/harness/defects.js +118 -0
  446. package/dist/harness/defects.js.map +1 -0
  447. package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
  448. package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
  449. package/dist/harness/gates/audit-gate-terms.js +72 -0
  450. package/dist/harness/gates/audit-gate-terms.js.map +1 -0
  451. package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
  452. package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
  453. package/dist/harness/gates/audit-gate-verdict.js +101 -0
  454. package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
  455. package/dist/harness/gates/audit-input-hash.d.ts +4 -0
  456. package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
  457. package/dist/harness/gates/audit-input-hash.js +96 -0
  458. package/dist/harness/gates/audit-input-hash.js.map +1 -0
  459. package/dist/harness/gates/audit-report-verification.d.ts +32 -0
  460. package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
  461. package/dist/harness/gates/audit-report-verification.js +207 -0
  462. package/dist/harness/gates/audit-report-verification.js.map +1 -0
  463. package/dist/harness/gates/decisions-hash.d.ts +13 -0
  464. package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
  465. package/dist/harness/gates/decisions-hash.js +72 -0
  466. package/dist/harness/gates/decisions-hash.js.map +1 -0
  467. package/dist/harness/gates/delivery-checklist.d.ts +21 -0
  468. package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
  469. package/dist/harness/gates/delivery-checklist.js +128 -0
  470. package/dist/harness/gates/delivery-checklist.js.map +1 -0
  471. package/dist/harness/gates/delivery-decision-gate.d.ts +29 -0
  472. package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
  473. package/dist/harness/gates/delivery-decision-gate.js +91 -0
  474. package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
  475. package/dist/harness/gates/delivery-decision-step.d.ts +16 -0
  476. package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
  477. package/dist/harness/gates/delivery-decision-step.js +71 -0
  478. package/dist/harness/gates/delivery-decision-step.js.map +1 -0
  479. package/dist/harness/gates/gate-override-report.d.ts +59 -0
  480. package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
  481. package/dist/harness/gates/gate-override-report.js +86 -0
  482. package/dist/harness/gates/gate-override-report.js.map +1 -0
  483. package/dist/harness/gates/gate-override-scope.d.ts +18 -0
  484. package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
  485. package/dist/harness/gates/gate-override-scope.js +98 -0
  486. package/dist/harness/gates/gate-override-scope.js.map +1 -0
  487. package/dist/harness/gates/gate-override-validation.d.ts +25 -0
  488. package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
  489. package/dist/harness/gates/gate-override-validation.js +81 -0
  490. package/dist/harness/gates/gate-override-validation.js.map +1 -0
  491. package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
  492. package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
  493. package/dist/harness/gates/gate-rule-catalog.js +112 -0
  494. package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
  495. package/dist/harness/gates/gate-terms.d.ts +33 -0
  496. package/dist/harness/gates/gate-terms.d.ts.map +1 -0
  497. package/dist/harness/gates/gate-terms.js +41 -0
  498. package/dist/harness/gates/gate-terms.js.map +1 -0
  499. package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
  500. package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
  501. package/dist/harness/gates/in-run-decision-check.js +138 -0
  502. package/dist/harness/gates/in-run-decision-check.js.map +1 -0
  503. package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
  504. package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
  505. package/dist/harness/gates/journey-gate-inputs.js +81 -0
  506. package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
  507. package/dist/harness/gates/migrated-waiver-review.d.ts +6 -0
  508. package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
  509. package/dist/harness/gates/migrated-waiver-review.js +116 -0
  510. package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
  511. package/dist/harness/gates/resolve-rule.d.ts +59 -0
  512. package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
  513. package/dist/harness/gates/resolve-rule.js +41 -0
  514. package/dist/harness/gates/resolve-rule.js.map +1 -0
  515. package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
  516. package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
  517. package/dist/harness/gates/unit-gate-overrides.js +122 -0
  518. package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
  519. package/dist/harness/journey.d.ts +24 -1
  520. package/dist/harness/journey.d.ts.map +1 -1
  521. package/dist/harness/journey.js +114 -23
  522. package/dist/harness/journey.js.map +1 -1
  523. package/dist/harness/manifest.d.ts +12 -0
  524. package/dist/harness/manifest.d.ts.map +1 -1
  525. package/dist/harness/manifest.js +73 -45
  526. package/dist/harness/manifest.js.map +1 -1
  527. package/dist/harness/next-step.d.ts +37 -6
  528. package/dist/harness/next-step.d.ts.map +1 -1
  529. package/dist/harness/next-step.js +34 -174
  530. package/dist/harness/next-step.js.map +1 -1
  531. package/dist/harness/parse.d.ts +25 -0
  532. package/dist/harness/parse.d.ts.map +1 -1
  533. package/dist/harness/parse.js +38 -13
  534. package/dist/harness/parse.js.map +1 -1
  535. package/dist/harness/script-check.d.ts.map +1 -1
  536. package/dist/harness/script-check.js +29 -6
  537. package/dist/harness/script-check.js.map +1 -1
  538. package/dist/harness/sensors.d.ts +14 -3
  539. package/dist/harness/sensors.d.ts.map +1 -1
  540. package/dist/harness/sensors.js +64 -44
  541. package/dist/harness/sensors.js.map +1 -1
  542. package/dist/harness/triage-signals.d.ts +20 -0
  543. package/dist/harness/triage-signals.d.ts.map +1 -0
  544. package/dist/harness/triage-signals.js +30 -0
  545. package/dist/harness/triage-signals.js.map +1 -0
  546. package/dist/harness/triage-unit.d.ts +23 -0
  547. package/dist/harness/triage-unit.d.ts.map +1 -0
  548. package/dist/harness/triage-unit.js +142 -0
  549. package/dist/harness/triage-unit.js.map +1 -0
  550. package/dist/harness/triage.d.ts +28 -0
  551. package/dist/harness/triage.d.ts.map +1 -0
  552. package/dist/harness/triage.js +99 -0
  553. package/dist/harness/triage.js.map +1 -0
  554. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  555. package/dist/harness/viewpoint-ledger.js +2 -1
  556. package/dist/harness/viewpoint-ledger.js.map +1 -1
  557. package/dist/hooks/bash-write-targets.d.ts +15 -0
  558. package/dist/hooks/bash-write-targets.d.ts.map +1 -0
  559. package/dist/hooks/bash-write-targets.js +173 -0
  560. package/dist/hooks/bash-write-targets.js.map +1 -0
  561. package/dist/hooks/claude-hook-entry.d.ts +11 -0
  562. package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
  563. package/dist/hooks/claude-hook-entry.js +137 -0
  564. package/dist/hooks/claude-hook-entry.js.map +1 -0
  565. package/dist/hooks/claude-transcript-turn.d.ts +11 -0
  566. package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
  567. package/dist/hooks/claude-transcript-turn.js +143 -0
  568. package/dist/hooks/claude-transcript-turn.js.map +1 -0
  569. package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
  570. package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
  571. package/dist/hooks/guard-delivery-contract-rule.js +152 -0
  572. package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
  573. package/dist/hooks/guard-protected-paths.d.ts +9 -0
  574. package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
  575. package/dist/hooks/guard-protected-paths.js +106 -0
  576. package/dist/hooks/guard-protected-paths.js.map +1 -0
  577. package/dist/hooks/guard-rules.d.ts +37 -0
  578. package/dist/hooks/guard-rules.d.ts.map +1 -0
  579. package/dist/hooks/guard-rules.js +61 -0
  580. package/dist/hooks/guard-rules.js.map +1 -0
  581. package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
  582. package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
  583. package/dist/hooks/guard-tty-spoof-rule.js +21 -0
  584. package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
  585. package/dist/hooks/hand-back-core.d.ts +37 -0
  586. package/dist/hooks/hand-back-core.d.ts.map +1 -0
  587. package/dist/hooks/hand-back-core.js +115 -0
  588. package/dist/hooks/hand-back-core.js.map +1 -0
  589. package/dist/hooks/hook-cli-command.d.ts +9 -0
  590. package/dist/hooks/hook-cli-command.d.ts.map +1 -0
  591. package/dist/hooks/hook-cli-command.js +46 -0
  592. package/dist/hooks/hook-cli-command.js.map +1 -0
  593. package/dist/hooks/hook-registry.d.ts +10 -0
  594. package/dist/hooks/hook-registry.d.ts.map +1 -0
  595. package/dist/hooks/hook-registry.js +24 -0
  596. package/dist/hooks/hook-registry.js.map +1 -0
  597. package/dist/hooks/hook-result-contract.d.ts +50 -0
  598. package/dist/hooks/hook-result-contract.d.ts.map +1 -0
  599. package/dist/hooks/hook-result-contract.js +80 -0
  600. package/dist/hooks/hook-result-contract.js.map +1 -0
  601. package/dist/hooks/hook-toggles.d.ts +10 -0
  602. package/dist/hooks/hook-toggles.d.ts.map +1 -0
  603. package/dist/hooks/hook-toggles.js +107 -0
  604. package/dist/hooks/hook-toggles.js.map +1 -0
  605. package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
  606. package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
  607. package/dist/hooks/pre-tool-guard-hook.js +105 -0
  608. package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
  609. package/dist/hooks/session-start-unit-state-hook.d.ts +13 -0
  610. package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
  611. package/dist/hooks/session-start-unit-state-hook.js +55 -0
  612. package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
  613. package/dist/hooks/stop-next-step-hook.d.ts +16 -0
  614. package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
  615. package/dist/hooks/stop-next-step-hook.js +42 -0
  616. package/dist/hooks/stop-next-step-hook.js.map +1 -0
  617. package/dist/hooks/sungen-project-root.d.ts +19 -0
  618. package/dist/hooks/sungen-project-root.d.ts.map +1 -0
  619. package/dist/hooks/sungen-project-root.js +130 -0
  620. package/dist/hooks/sungen-project-root.js.map +1 -0
  621. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
  622. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
  623. package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
  624. package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
  625. package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
  626. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
  627. package/dist/orchestrator/ai-skills/commands.js +16 -6
  628. package/dist/orchestrator/ai-skills/commands.js.map +1 -1
  629. package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
  630. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -1
  631. package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
  632. package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
  633. package/dist/orchestrator/claude-hooks-install.js +203 -0
  634. package/dist/orchestrator/claude-hooks-install.js.map +1 -0
  635. package/dist/orchestrator/lockstep-version.d.ts +15 -5
  636. package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
  637. package/dist/orchestrator/lockstep-version.js +30 -17
  638. package/dist/orchestrator/lockstep-version.js.map +1 -1
  639. package/dist/orchestrator/project-initializer.d.ts +6 -0
  640. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  641. package/dist/orchestrator/project-initializer.js +23 -0
  642. package/dist/orchestrator/project-initializer.js.map +1 -1
  643. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  644. package/dist/orchestrator/screen-manager.js +4 -2
  645. package/dist/orchestrator/screen-manager.js.map +1 -1
  646. package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  647. package/dist/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  648. package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  649. package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  650. package/dist/orchestrator/templates/ai-src/commands/create-test.md +14 -16
  651. package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  652. package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  653. package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
  654. package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  655. package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
  656. package/dist/orchestrator/templates/ai-src/commands/review.md +2 -0
  657. package/dist/orchestrator/templates/ai-src/commands/run-test.md +14 -10
  658. package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
  659. package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
  660. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  661. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  662. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  663. package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  664. package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  665. package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  666. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
  667. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
  668. package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
  669. package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  670. package/dist/utils/safe-yaml-parse.d.ts +8 -0
  671. package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
  672. package/dist/utils/safe-yaml-parse.js +51 -0
  673. package/dist/utils/safe-yaml-parse.js.map +1 -0
  674. package/package.json +3 -3
  675. package/src/basis/anchor-grammar.ts +83 -0
  676. package/src/basis/anchor-hash.ts +27 -0
  677. package/src/basis/bm51-tables.ts +65 -0
  678. package/src/basis/extract-spec-anchors.ts +95 -0
  679. package/src/basis/migration-sentinel.ts +175 -0
  680. package/src/basis/spec-frontmatter.ts +68 -0
  681. package/src/basis/spec-sections.ts +35 -0
  682. package/src/basis/test-basis-schema.ts +81 -0
  683. package/src/capabilities/registry.ts +16 -0
  684. package/src/cli/commands/audit.ts +9 -4
  685. package/src/cli/commands/capability.ts +4 -2
  686. package/src/cli/commands/contract-derive-command.ts +60 -0
  687. package/src/cli/commands/contract.ts +130 -0
  688. package/src/cli/commands/defect.ts +49 -0
  689. package/src/cli/commands/delivery.ts +85 -13
  690. package/src/cli/commands/generate.ts +3 -1
  691. package/src/cli/commands/journey.ts +39 -14
  692. package/src/cli/commands/next.ts +60 -8
  693. package/src/cli/commands/triage.ts +57 -0
  694. package/src/cli/commands/update.ts +35 -8
  695. package/src/cli/index.ts +24 -30
  696. package/src/contract/approve-contract.ts +94 -0
  697. package/src/contract/assertion-matcher-vocabulary.ts +93 -0
  698. package/src/contract/build-contract-verdict.ts +108 -0
  699. package/src/contract/build-trace-conformance.ts +100 -0
  700. package/src/contract/classify-contract-change.ts +92 -0
  701. package/src/contract/classify-contract-unit.ts +47 -0
  702. package/src/contract/classify-trace-conformance.ts +116 -0
  703. package/src/contract/contract-git.ts +106 -0
  704. package/src/contract/contract-hash.ts +34 -0
  705. package/src/contract/contract-report-file.ts +25 -0
  706. package/src/contract/contract-schema.ts +137 -0
  707. package/src/contract/contract-units.ts +52 -0
  708. package/src/contract/contracts-lock-file.ts +97 -0
  709. package/src/contract/data-normalize.ts +66 -0
  710. package/src/contract/derive-free-feature.ts +110 -0
  711. package/src/contract/field-semantic-redaction.ts +80 -0
  712. package/src/contract/free-feature-blocks.ts +56 -0
  713. package/src/contract/lock-secret-guard.ts +95 -0
  714. package/src/contract/lock-vs-git-head.ts +38 -0
  715. package/src/contract/normalize-contract-step.ts +74 -0
  716. package/src/contract/normalize-feature-steps.ts +93 -0
  717. package/src/contract/oracle-strength.ts +80 -0
  718. package/src/contract/project-contract-blockers.ts +36 -0
  719. package/src/contract/record-contract-approval.ts +70 -0
  720. package/src/contract/render-free-gherkin.ts +113 -0
  721. package/src/contract/render-trace-sentence.ts +98 -0
  722. package/src/contract/render-trace-target.ts +68 -0
  723. package/src/contract/resolve-unit-trace-identities.ts +35 -0
  724. package/src/contract/trace/collect-trace-evidence.ts +109 -0
  725. package/src/contract/trace/identity-cache.ts +61 -0
  726. package/src/contract/trace/select-final-attempt.ts +70 -0
  727. package/src/contract/trace/snapshot-html-renderer.ts +136 -0
  728. package/src/contract/trace/snapshot-resolver.ts +66 -0
  729. package/src/contract/trace/snapshot-store.ts +151 -0
  730. package/src/contract/trace/target-identity.ts +104 -0
  731. package/src/contract/trace/test-step-join.ts +103 -0
  732. package/src/contract/trace/trace-binding.ts +56 -0
  733. package/src/contract/trace/trace-file-reader.ts +123 -0
  734. package/src/contract/trace/trace-fingerprint.ts +68 -0
  735. package/src/contract/trace/trace-identity-resolution.ts +74 -0
  736. package/src/contract/trace/trace-run-record.ts +74 -0
  737. package/src/contract/trace/trace-steps.ts +88 -0
  738. package/src/contract/trace/trace-zip-limits.ts +73 -0
  739. package/src/contract/trace/trace-zip-reader.ts +127 -0
  740. package/src/contract/verify-approval-decisions.ts +60 -0
  741. package/src/decisions/append-decision.ts +35 -0
  742. package/src/decisions/confirmed-decision-commands.ts +128 -0
  743. package/src/decisions/decision-file.ts +93 -0
  744. package/src/decisions/decision-identity.ts +73 -0
  745. package/src/decisions/decision-integrity-check.ts +61 -0
  746. package/src/decisions/decision-schema.ts +76 -0
  747. package/src/decisions/decision-source.ts +14 -0
  748. package/src/decisions/exclusive-file-create.ts +42 -0
  749. package/src/decisions/git-head-trust.ts +32 -0
  750. package/src/decisions/human-confirmation.ts +114 -0
  751. package/src/decisions/legacy-waiver-migration.ts +135 -0
  752. package/src/decisions/read-decisions.ts +61 -0
  753. package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
  754. package/src/decisions/waiver-decisions.ts +56 -0
  755. package/src/exporters/csv-exporter.ts +5 -1
  756. package/src/exporters/feature-parser.ts +12 -0
  757. package/src/exporters/matrix/build.ts +62 -18
  758. package/src/exporters/matrix/carry-manual-results.ts +70 -7
  759. package/src/exporters/matrix/export.ts +17 -2
  760. package/src/exporters/matrix/gates.ts +62 -129
  761. package/src/exporters/matrix/render-csv.ts +4 -1
  762. package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
  763. package/src/exporters/matrix/render-xlsx.ts +6 -0
  764. package/src/exporters/matrix/types.ts +9 -1
  765. package/src/exporters/playwright-report-parser.ts +1 -0
  766. package/src/exporters/scenario-merger.ts +2 -1
  767. package/src/exporters/types.ts +5 -0
  768. package/src/generators/test-generator/code-generator.ts +13 -0
  769. package/src/graph/evaluate-graph.ts +62 -0
  770. package/src/graph/locate-finding.ts +93 -0
  771. package/src/graph/node-decl.ts +65 -0
  772. package/src/graph/nodes/core-node-catalog.ts +15 -0
  773. package/src/graph/nodes/test-case-contract-nodes.ts +69 -0
  774. package/src/graph/nodes/test-case-nodes.ts +68 -0
  775. package/src/graph/nodes/unit-audit-nodes.ts +95 -0
  776. package/src/graph/nodes/unit-contract-node.ts +82 -0
  777. package/src/graph/nodes/unit-design-nodes.ts +88 -0
  778. package/src/graph/nodes/unit-execution-nodes.ts +77 -0
  779. package/src/graph/test-case-identity.ts +75 -0
  780. package/src/graph/test-case-results.ts +90 -0
  781. package/src/graph/test-case-satisfied.ts +55 -0
  782. package/src/graph/test-case-state-shape.ts +62 -0
  783. package/src/graph/test-case-states.ts +84 -0
  784. package/src/graph/unit-artifact-probes.ts +77 -0
  785. package/src/graph/unit-state-facts.ts +112 -0
  786. package/src/harness/audit.ts +46 -11
  787. package/src/harness/catalog/gate-rules.yaml +647 -0
  788. package/src/harness/counted-assertions.ts +33 -0
  789. package/src/harness/defect-draft.ts +115 -0
  790. package/src/harness/defects.ts +94 -0
  791. package/src/harness/gates/audit-gate-terms.ts +80 -0
  792. package/src/harness/gates/audit-gate-verdict.ts +78 -0
  793. package/src/harness/gates/audit-input-hash.ts +55 -0
  794. package/src/harness/gates/audit-report-verification.ts +173 -0
  795. package/src/harness/gates/decisions-hash.ts +42 -0
  796. package/src/harness/gates/delivery-checklist.ts +120 -0
  797. package/src/harness/gates/delivery-decision-gate.ts +111 -0
  798. package/src/harness/gates/delivery-decision-step.ts +73 -0
  799. package/src/harness/gates/gate-override-report.ts +114 -0
  800. package/src/harness/gates/gate-override-scope.ts +66 -0
  801. package/src/harness/gates/gate-override-validation.ts +75 -0
  802. package/src/harness/gates/gate-rule-catalog.ts +82 -0
  803. package/src/harness/gates/gate-terms.ts +59 -0
  804. package/src/harness/gates/in-run-decision-check.ts +99 -0
  805. package/src/harness/gates/journey-gate-inputs.ts +89 -0
  806. package/src/harness/gates/migrated-waiver-review.ts +68 -0
  807. package/src/harness/gates/resolve-rule.ts +94 -0
  808. package/src/harness/gates/unit-gate-overrides.ts +99 -0
  809. package/src/harness/journey.ts +122 -27
  810. package/src/harness/manifest.ts +69 -45
  811. package/src/harness/next-step.ts +60 -170
  812. package/src/harness/parse.ts +40 -13
  813. package/src/harness/script-check.ts +27 -6
  814. package/src/harness/sensors.ts +73 -47
  815. package/src/harness/triage-signals.ts +46 -0
  816. package/src/harness/triage-unit.ts +113 -0
  817. package/src/harness/triage.ts +127 -0
  818. package/src/harness/viewpoint-ledger.ts +2 -2
  819. package/src/hooks/bash-write-targets.ts +109 -0
  820. package/src/hooks/claude-hook-entry.ts +85 -0
  821. package/src/hooks/claude-transcript-turn.ts +96 -0
  822. package/src/hooks/guard-delivery-contract-rule.ts +110 -0
  823. package/src/hooks/guard-protected-paths.ts +70 -0
  824. package/src/hooks/guard-rules.ts +98 -0
  825. package/src/hooks/guard-tty-spoof-rule.ts +31 -0
  826. package/src/hooks/hand-back-core.ts +106 -0
  827. package/src/hooks/hook-cli-command.ts +17 -0
  828. package/src/hooks/hook-registry.ts +24 -0
  829. package/src/hooks/hook-result-contract.ts +114 -0
  830. package/src/hooks/hook-toggles.ts +66 -0
  831. package/src/hooks/pre-tool-guard-hook.ts +67 -0
  832. package/src/hooks/session-start-unit-state-hook.ts +54 -0
  833. package/src/hooks/stop-next-step-hook.ts +37 -0
  834. package/src/hooks/sungen-project-root.ts +80 -0
  835. package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
  836. package/src/orchestrator/ai-skills/commands.ts +22 -8
  837. package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
  838. package/src/orchestrator/claude-hooks-install.ts +168 -0
  839. package/src/orchestrator/lockstep-version.ts +28 -15
  840. package/src/orchestrator/project-initializer.ts +21 -0
  841. package/src/orchestrator/screen-manager.ts +3 -1
  842. package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  843. package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  844. package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  845. package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  846. package/src/orchestrator/templates/ai-src/commands/create-test.md +14 -16
  847. package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  848. package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  849. package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
  850. package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  851. package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
  852. package/src/orchestrator/templates/ai-src/commands/review.md +2 -0
  853. package/src/orchestrator/templates/ai-src/commands/run-test.md +14 -10
  854. package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
  855. package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
  856. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  857. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  858. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  859. package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  860. package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  861. package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  862. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +2 -0
  863. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +1 -1
  864. package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +2 -0
  865. package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  866. package/src/utils/safe-yaml-parse.ts +44 -0
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The contract verdict of one unit, BUILT BY CODE from raw inputs every time (DOS-14.0, takumi T3):
3
+ * the `.feature` files, the working lock, the lock and the approval decisions in git HEAD. No report
4
+ * file is read — the `.sungen/reports/<slug>-contract.json` that `contract check` writes is output
5
+ * only, so a hand-edited "pass" there changes nothing. The CLI and the Claude Code guard call this
6
+ * same function, so they cannot disagree.
7
+ *
8
+ * fail oracle-weakened / target-changed / assertion-lost, or any lock-uncommitted issue
9
+ * (added · changed · removed vs HEAD, tampered, unverified), or an APPROVED free /
10
+ * impl:code scenario with no admissible trace (unproven, also past the deadline) exit 2
11
+ * review any hash-changed (changed, not weaker — QA re-approves), opaque (a check no code
12
+ * can compare, or a target identity that is only `low`), or inconclusive (an
13
+ * approved free scenario's final attempt did not pass); blocks delivery exit 0
14
+ * pass only match / strengthened / proposed / nondeterministic exit 0
15
+ *
16
+ * free / impl:code scenarios are ALSO held against their recorded traces (DOS-14.6,
17
+ * build-trace-conformance.ts): the worse of the Gherkin class and the trace class wins. A
18
+ * proposed (unapproved) one's trace problems are warnings — it is not locked yet.
19
+ *
20
+ * git failing in a checkout that has .git is not "no git": with a lock present the verdict cannot be
21
+ * built (GitUnavailableError → CLI exit 1, guard `ask`); without one it is only a warning.
22
+ */
23
+ import * as fs from 'fs';
24
+ import * as path from 'path';
25
+ import { canonicalJson, sha256Hex } from './contract-hash';
26
+ import { CLI_GIT_TIMEOUT_MS, GitUnavailableError, HeadSnapshot, headSnapshot, readTextOrNull } from './contract-git';
27
+ import { HARD_CLASSES, REVIEW_CLASSES } from './contract-schema';
28
+ import { ScenarioResult, severityOf } from './classify-contract-change';
29
+ import { traceConformanceForUnit } from './build-trace-conformance';
30
+ import type { TraceOutcome } from './classify-trace-conformance';
31
+ import { classifyUnit } from './classify-contract-unit';
32
+ import { lockPathFor, parseLockText } from './contracts-lock-file';
33
+ import { compareLockWithHead, UncommittedEntry } from './lock-vs-git-head';
34
+ import { contractFeatureFiles, loadContractScenarios } from './normalize-feature-steps';
35
+ import { unverifiedApprovals } from './verify-approval-decisions';
36
+
37
+ export { classifyUnit } from './classify-contract-unit';
38
+
39
+ export interface ContractUnit { name: string; dir: string }
40
+ export type VerdictValue = 'pass' | 'review' | 'fail';
41
+
42
+ export interface ContractVerdict {
43
+ schema: 1;
44
+ unit: string;
45
+ verdict: VerdictValue;
46
+ exitCode: 0 | 2;
47
+ /** sha256 over the inputs the verdict was built from: features, working lock, lock issues. */
48
+ inputsHash: string;
49
+ scenarios: ScenarioResult[];
50
+ /** Every lock entry that is not what QA approved and committed (rule contract.lock-uncommitted). */
51
+ uncommitted: UncommittedEntry[];
52
+ warnings: string[];
53
+ }
54
+
55
+ /**
56
+ * `snapshot` lets a caller checking many units read HEAD once; `gitTimeoutMs` bounds each git call;
57
+ * `traceBudgetMs` bounds the trace part (default ~3 s for the guard; `contract check` passes more).
58
+ */
59
+ export interface VerdictOptions { snapshot?: HeadSnapshot; gitTimeoutMs?: number; traceBudgetMs?: number }
60
+
61
+ /** Fold a free / impl:code scenario's trace outcome into its Gherkin result: the worse class wins. */
62
+ function withTrace(results: ScenarioResult[], outcomes: Map<string, TraceOutcome>): ScenarioResult[] {
63
+ return results.map((r) => {
64
+ const o = outcomes.get(r.id);
65
+ if (!o) return r;
66
+ const cls = severityOf(o.class) > severityOf(r.class) ? o.class : r.class;
67
+ return { ...r, class: cls, findings: [...r.findings, ...o.findings], trace: { hashes: o.hashes, runs: o.runs } };
68
+ });
69
+ }
70
+
71
+ export function buildContractVerdict(projectRoot: string, unit: ContractUnit, opts: VerdictOptions = {}): ContractVerdict {
72
+ const snap = opts.snapshot ?? headSnapshot(projectRoot, opts.gitTimeoutMs ?? CLI_GIT_TIMEOUT_MS);
73
+ const files = contractFeatureFiles(unit.dir, unit.name);
74
+ const current = loadContractScenarios(unit.dir, unit.name);
75
+ const lockFile = lockPathFor(unit.dir);
76
+ const lockText = readTextOrNull(lockFile);
77
+ if (snap.error && lockText !== null) throw new GitUnavailableError(snap.error);
78
+ const lock = lockText === null ? null : parseLockText(lockText, lockFile);
79
+ const tampered = new Set(lock?.tampered ?? []);
80
+ const gherkin = classifyUnit(lock, current, tampered);
81
+ const traced = traceConformanceForUnit(projectRoot, unit, current, lock, tampered, { budgetMs: opts.traceBudgetMs });
82
+ const results = withTrace(gherkin.results, traced.outcomes);
83
+ const warnings = [...gherkin.warnings, ...traced.warnings];
84
+ const head = compareLockWithHead(projectRoot, lockFile, lockText, lock, snap);
85
+ if (head.status === 'no-git') {
86
+ warnings.push(snap.error
87
+ ? `git failed (${snap.error}); this unit has no lock, so nothing was compared with git HEAD`
88
+ : 'not a git checkout with a commit: the lock was not compared with git HEAD (working tree used)');
89
+ }
90
+ const uncommitted: UncommittedEntry[] = head.status === 'compared' ? [...head.uncommitted] : [];
91
+ for (const id of tampered) {
92
+ if (!uncommitted.some((u) => u.id === id)) uncommitted.push({ id, change: 'tampered', detail: 'its steps no longer hash to its `hash` — edited by hand' });
93
+ }
94
+ uncommitted.push(...unverifiedApprovals(projectRoot, unit.dir, lock, new Set(uncommitted.map((u) => u.id)), snap));
95
+
96
+ const hard = uncommitted.length > 0 || results.some((r) => HARD_CLASSES.has(r.class));
97
+ const verdict: VerdictValue = hard ? 'fail' : results.some((r) => REVIEW_CLASSES.has(r.class)) ? 'review' : 'pass';
98
+ const rel = (f: string): string => path.relative(projectRoot, f).split(path.sep).join('/');
99
+ const inputsHash = sha256Hex(canonicalJson({
100
+ features: files.map((f) => [rel(f), sha256Hex(fs.readFileSync(f, 'utf8'))]),
101
+ lock: lockText === null ? null : sha256Hex(lockText),
102
+ head: head.status,
103
+ uncommitted,
104
+ ...(traced.inputs === null ? {} : { trace: traced.inputs }),
105
+ }));
106
+ const scenarios = results.map((r) => (r.file ? { ...r, file: rel(r.file) } : r));
107
+ return { schema: 1, unit: unit.name, verdict, exitCode: verdict === 'fail' ? 2 : 0, inputsHash, scenarios, uncommitted, warnings };
108
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Trace conformance of one unit, built by code every time (DOS-14.0, 14.6; takumi T3) — the part of
3
+ * the contract verdict that only free / impl:code scenarios have. Synchronous and read-only, so
4
+ * `contract check` and the Claude Code delivery guard (both via buildContractVerdict) agree.
5
+ *
6
+ * platform mobile not supported yet: a warning, nothing judged, exit unaffected
7
+ * no free / impl:code nothing to do (the verdict is byte-identical to the Gherkin-only one)
8
+ * otherwise evidence (run records → reports → final attempts → bound traces) →
9
+ * identities (driver cache or low heuristic) → render → ONE batched
10
+ * fingerprint parse → the ladder
11
+ *
12
+ * Bounded (review H4, M2): a deadline (`budgetMs`; the guard's default is ~3 s) — a scenario not
13
+ * judged in time is `unproven` when approved, a warning while proposed: fail CLOSED. An input that
14
+ * cannot be read while judging (test-data, a trace) makes that scenario unproven with its reason,
15
+ * never a thrown verdict. A trace older than the unit's newest `.feature` / lock is refused (stale).
16
+ */
17
+ import { createHash } from 'crypto';
18
+ import * as fs from 'fs';
19
+ import { readCapabilities } from '../harness/capability';
20
+ import { classifyTraceRuns, notJudged, TraceOutcome } from './classify-trace-conformance';
21
+ import { ContractLock, ContractScenario, isHumanApproved, LockEntry } from './contract-schema';
22
+ import { lockPathFor } from './contracts-lock-file';
23
+ import { loadTestDataIndex, TestDataIndex } from './data-normalize';
24
+ import { contractFeatureFiles } from './normalize-feature-steps';
25
+ import { renderTraceSteps, RenderedStep } from './render-free-gherkin';
26
+ import { collectScenarioEvidence, OVER_BUDGET, ScenarioEvidence, TraceRun } from './trace/collect-trace-evidence';
27
+
28
+ export { OVER_BUDGET } from './trace/collect-trace-evidence';
29
+ import { identityCachePath, readIdentityCache } from './trace/identity-cache';
30
+ import { identitiesFor } from './trace/trace-identity-resolution';
31
+ import { fingerprintsOf, TraceFingerprint } from './trace/trace-fingerprint';
32
+ import { traceIntentSteps } from './trace/trace-steps';
33
+
34
+ export interface UnitTraceConformance { outcomes: Map<string, TraceOutcome>; warnings: string[]; inputs: unknown }
35
+
36
+ const mtimeMs = (f: string): number => { try { return fs.statSync(f).mtimeMs; } catch { return 0; } };
37
+ const sha = (s: string | Buffer): string => createHash('sha256').update(s).digest('hex');
38
+ const why = (e: unknown): string => (e instanceof Error ? e.message : String(e)).split('\n')[0].slice(0, 300);
39
+
40
+ export const MOBILE_NOT_SUPPORTED = 'trace conformance is web-only: free / impl:code scenarios of a mobile project are not supported yet (not judged)';
41
+ /** The guard path's deadline for the trace part of one unit's verdict (override: SUNGEN_TRACE_VERDICT_BUDGET_MS). */
42
+ export function defaultTraceBudgetMs(env: Record<string, string | undefined> = process.env): number {
43
+ const v = Number(env.SUNGEN_TRACE_VERDICT_BUDGET_MS);
44
+ return Number.isFinite(v) && v > 0 ? v : 3_000;
45
+ }
46
+
47
+ export function traceConformanceForUnit(
48
+ projectRoot: string, unit: { name: string; dir: string }, current: ContractScenario[], lock: ContractLock | null,
49
+ untrusted: ReadonlySet<string>, opts: { budgetMs?: number } = {},
50
+ ): UnitTraceConformance {
51
+ const deadline = Date.now() + (opts.budgetMs ?? defaultTraceBudgetMs());
52
+ const traced = [...new Map(current.filter((s) => s.implementation !== 'compiled').map((s) => [s.id, s])).values()];
53
+ if (!traced.length) return { outcomes: new Map(), warnings: [], inputs: null };
54
+ if ((readCapabilities(projectRoot).platform ?? '').toLowerCase() === 'mobile') return { outcomes: new Map(), warnings: [MOBILE_NOT_SUPPORTED], inputs: { platform: 'mobile' } };
55
+
56
+ const approvedEntry = (id: string): LockEntry | undefined => { const e = lock?.scenarios[id]; return isHumanApproved(e) && !untrusted.has(id) ? e : undefined; };
57
+ const outcomes = new Map<string, TraceOutcome>();
58
+ const warnings: string[] = [];
59
+ let evidence: ScenarioEvidence = { runs: new Map(), problems: [], inputs: [] };
60
+ let index: TestDataIndex = { byValue: new Map() };
61
+ let setupError = '';
62
+ try {
63
+ const notBefore = Math.max(mtimeMs(lockPathFor(unit.dir)), ...contractFeatureFiles(unit.dir, unit.name).map(mtimeMs));
64
+ evidence = collectScenarioEvidence(projectRoot, new Set(traced.map((s) => s.id)), notBefore, deadline);
65
+ index = loadTestDataIndex(unit.dir, unit.name);
66
+ } catch (e) { setupError = `trace conformance could not run: ${why(e)}`; }
67
+ const { cache, problem } = readIdentityCache(projectRoot);
68
+ warnings.push(...evidence.problems, ...(problem ? [problem] : []));
69
+
70
+ // 1. render every admissible run (deadline checked per scenario), 2. ONE fingerprint parse, 3. ladder.
71
+ const pending: Array<{ sc: ContractScenario; runs: TraceRun[]; rendered: Map<TraceRun, RenderedStep[]> }> = [];
72
+ for (const sc of traced) {
73
+ if (setupError) { outcomes.set(sc.id, notJudged(sc.id, !!approvedEntry(sc.id), 'unproven', [setupError])); continue; }
74
+ if (Date.now() > deadline) { outcomes.set(sc.id, notJudged(sc.id, !!approvedEntry(sc.id), 'unproven', [OVER_BUDGET])); continue; }
75
+ const runs = evidence.runs.get(sc.id) ?? [];
76
+ const rendered = new Map<TraceRun, RenderedStep[]>();
77
+ try {
78
+ for (const run of runs.filter((r) => r.status === 'admissible' && r.trace)) {
79
+ const steps = traceIntentSteps(run.trace!);
80
+ rendered.set(run, renderTraceSteps(steps, identitiesFor(run.trace!, steps, cache), { index, projectRoot, specFile: run.specFile, secrets: new Set() }));
81
+ }
82
+ pending.push({ sc, runs, rendered });
83
+ } catch (e) { outcomes.set(sc.id, notJudged(sc.id, !!approvedEntry(sc.id), 'unproven', [`trace conformance failed: ${why(e)}`])); }
84
+ }
85
+ const items = pending.flatMap((p) => [...p.rendered.entries()].map(([run, rendered]) => ({ p, run, rendered, implementation: p.sc.implementation })));
86
+ let fps: TraceFingerprint[] = [];
87
+ try { fps = fingerprintsOf(items); } catch (e) {
88
+ for (const p of pending) outcomes.set(p.sc.id, notJudged(p.sc.id, !!approvedEntry(p.sc.id), 'unproven', [`trace conformance failed: ${why(e)}`]));
89
+ }
90
+ if (fps.length === items.length) {
91
+ for (const p of pending) {
92
+ const byRun = new Map(items.map((it, i) => [it, fps[i]] as const).filter(([it]) => it.p === p).map(([it, fp]) => [it.run, fp]));
93
+ outcomes.set(p.sc.id, classifyTraceRuns(p.sc.id, p.sc.implementation, approvedEntry(p.sc.id), p.runs, byRun));
94
+ }
95
+ }
96
+ for (const o of outcomes.values()) warnings.push(...o.warnings);
97
+ const cacheFile = identityCachePath(projectRoot);
98
+ const traceShas = [...evidence.runs.values()].flat().filter((r) => r.trace).map((r) => r.trace!.sha256).sort();
99
+ return { outcomes, warnings, inputs: { runs: evidence.inputs, traces: traceShas, identities: fs.existsSync(cacheFile) ? sha(fs.readFileSync(cacheFile)) : null } };
100
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * One approved lock entry against the scenario as it reads now → its class on the strength ladder
3
+ * (DOS-14.4) and the findings behind it.
4
+ *
5
+ * same hash match
6
+ * assertions, paired in three passes:
7
+ * 1. identical nothing to say
8
+ * 2. same target (ref + scope) compareAsserts against the WORST candidate: weakened · strengthened · review
9
+ * 3. same sentence, other target target-changed
10
+ * left over in the lock assertion-lost left over now strengthened (new check)
11
+ * actions changed · implementation retagged · only reordered hash-changed (review)
12
+ *
13
+ * The scenario's class is its most severe finding. A change that produced no finding at all is
14
+ * still `hash-changed`: equal hashes are the only road to `match`.
15
+ */
16
+ import { sameJson } from './contract-hash';
17
+ import { ContractScenario, ContractStep, FindingClass, LadderClass, LockEntry, RULE_OF, ScenarioClass, TraceClass } from './contract-schema';
18
+ import { compareAsserts } from './oracle-strength';
19
+
20
+ export interface ContractFinding { class: FindingClass; rule?: string; detail: string; step?: number }
21
+ /** What a free / impl:code scenario's traces fingerprinted to (DOS-14.6) — output only. */
22
+ export interface TraceSummary { hashes: string[]; runs: string[] }
23
+ export interface ScenarioResult { id: string; class: ScenarioClass; findings: ContractFinding[]; file?: string; trace?: TraceSummary }
24
+
25
+ const SEVERITY: Record<ScenarioClass, number> = {
26
+ proposed: 0, match: 0, strengthened: 1, nondeterministic: 1, 'hash-changed': 2, opaque: 2, inconclusive: 2,
27
+ 'target-changed': 3, 'oracle-weakened': 3, 'assertion-lost': 3, unproven: 3,
28
+ };
29
+ export const severityOf = (c: ScenarioClass): number => SEVERITY[c];
30
+
31
+ export const finding = (cls: Exclude<LadderClass, 'match'> | TraceClass, detail: string, step?: number): ContractFinding =>
32
+ ({ class: cls, ...(RULE_OF[cls] ? { rule: RULE_OF[cls] } : {}), detail, ...(step ? { step } : {}) });
33
+
34
+ type Indexed = { step: ContractStep; at: number };
35
+ const asserts = (steps: ContractStep[]): Indexed[] => steps.map((step, i) => ({ step, at: i + 1 })).filter((x) => x.step.kind === 'assert');
36
+ const targetKey = (s: ContractStep): string => s.target ? `${s.target.ref}\u0000${s.target.parent ?? ''}` : `\u0001${s.verb}`;
37
+ const label = (s: ContractStep): string => s.target ? `[${s.target.ref}]${s.target.parent ? ` in ${s.target.parent}` : ''}` : `"${s.verb}"`;
38
+
39
+ function compareAssertions(locked: ContractStep[], current: ContractStep[]): ContractFinding[] {
40
+ const out: ContractFinding[] = [];
41
+ const was = asserts(locked);
42
+ const now = asserts(current);
43
+ const used = new Set<number>();
44
+ const take = (pred: (c: Indexed) => boolean): Indexed | undefined => {
45
+ const hit = now.find((c) => !used.has(c.at) && pred(c));
46
+ if (hit) used.add(hit.at);
47
+ return hit;
48
+ };
49
+ const open: Indexed[] = [];
50
+ for (const l of was) if (!take((c) => sameJson(c.step, l.step))) open.push(l);
51
+ const unpaired: Indexed[] = [];
52
+ for (const l of open) {
53
+ const candidates = now.filter((c) => !used.has(c.at) && targetKey(c.step) === targetKey(l.step));
54
+ if (!candidates.length) { unpaired.push(l); continue; }
55
+ // The WORST replacement is the one judged: picking the kindest let `see [X]` → `is hidden` +
56
+ // `is empty` read as strengthened, the inversion hiding behind a harmless extra check.
57
+ const judged = candidates.map((c) => ({ c, r: compareAsserts(l.step, c.step) }));
58
+ const rank = (k: string): number => (k === 'oracle-weakened' ? 2 : k === 'review' ? 1 : 0);
59
+ const worst = judged.sort((a, b) => rank(b.r.class) - rank(a.r.class))[0];
60
+ used.add(worst.c.at);
61
+ const cls = worst.r.class === 'review' ? 'hash-changed' : worst.r.class;
62
+ out.push(finding(cls, `${label(l.step)}: ${worst.r.detail}`, worst.c.at));
63
+ }
64
+ for (const l of unpaired) {
65
+ const moved = take((c) => c.step.verb === l.step.verb && !!l.step.target && !!c.step.target);
66
+ out.push(moved
67
+ ? finding('target-changed', `the check "${l.step.verb}" moved from ${label(l.step)} to ${label(moved.step)}`, moved.at)
68
+ : finding('assertion-lost', `the approved check ${label(l.step)} "${l.step.verb}" (step ${l.at}) is gone`));
69
+ }
70
+ for (const c of now) if (!used.has(c.at)) out.push(finding('strengthened', `new check ${label(c.step)} "${c.step.verb}"`, c.at));
71
+ return out;
72
+ }
73
+
74
+ export function classifyScenario(id: string, entry: LockEntry, current: ContractScenario | undefined): ScenarioResult {
75
+ if (!current) {
76
+ return { id, class: 'assertion-lost', findings: [finding('assertion-lost', 'the approved scenario is no longer in the feature (removed, its ID changed, or made @manual)')] };
77
+ }
78
+ const base = { id, file: current.source.file };
79
+ if (current.hash === entry.hash) return { ...base, class: 'match', findings: [] };
80
+ const findings = compareAssertions(entry.steps, current.steps);
81
+ if (entry.implementation !== current.implementation) {
82
+ findings.push(finding('hash-changed', `implementation path changed: ${entry.implementation} → ${current.implementation} (retag)`));
83
+ }
84
+ const actionsOf = (steps: ContractStep[]) => steps.filter((s) => s.kind === 'action');
85
+ if (!sameJson(actionsOf(entry.steps), actionsOf(current.steps))) findings.push(finding('hash-changed', 'the actions changed (what the test does before it checks)'));
86
+ if (!findings.length) findings.push(finding('hash-changed', 'the steps changed without a weaker or stronger check (e.g. reordered)'));
87
+ const worst = findings.reduce<ScenarioClass>((acc, f) => {
88
+ const c = f.class as ScenarioClass;
89
+ return severityOf(c) > severityOf(acc) ? c : acc;
90
+ }, 'match');
91
+ return { ...base, class: worst, findings };
92
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Every scenario id of a unit, locked or current, with its ladder class (DOS-14.3, DOS-14.4).
3
+ *
4
+ * approved entry, one current copy classifyScenario (the ladder)
5
+ * approved entry, no current copy assertion-lost
6
+ * approved entry, several copies assertion-lost — the lock can no longer tell which copy QA
7
+ * approved, and picking one would let the others hide (M7)
8
+ * no approved entry proposed (never blocks); a tampered entry counts as none
9
+ * Scenario Outline (any lock state) at least hash-changed: the compiler does not support the
10
+ * construct, so the contract cannot judge its Examples
11
+ */
12
+ import { classifyScenario, finding, ScenarioResult, severityOf } from './classify-contract-change';
13
+ import { ContractLock, ContractScenario, isHumanApproved } from './contract-schema';
14
+
15
+ const OUTLINE = 'Scenario Outline is not supported by the compiler — the contract cannot judge its Examples rows; rewrite it as @cases data';
16
+
17
+ function withOutlineFinding(r: ScenarioResult): ScenarioResult {
18
+ const findings = [...r.findings, finding('hash-changed', OUTLINE)];
19
+ return { ...r, class: severityOf(r.class) >= severityOf('hash-changed') ? r.class : 'hash-changed', findings };
20
+ }
21
+
22
+ export function classifyUnit(
23
+ lock: ContractLock | null, current: ContractScenario[], untrusted: ReadonlySet<string> = new Set(),
24
+ ): { results: ScenarioResult[]; warnings: string[] } {
25
+ const groups = new Map<string, ContractScenario[]>();
26
+ for (const sc of current) groups.set(sc.id, [...(groups.get(sc.id) ?? []), sc]);
27
+ const approvedIds = Object.entries(lock?.scenarios ?? {}).filter(([id, e]) => isHumanApproved(e) && !untrusted.has(id)).map(([id]) => id);
28
+ const results: ScenarioResult[] = [];
29
+ const warnings: string[] = [];
30
+ for (const id of [...new Set([...approvedIds, ...groups.keys()])].sort((a, b) => a.localeCompare(b, 'en'))) {
31
+ const entry = lock?.scenarios[id];
32
+ const copies = groups.get(id) ?? [];
33
+ const approved = isHumanApproved(entry) && !untrusted.has(id);
34
+ let mine: ScenarioResult[];
35
+ if (!approved) {
36
+ if (copies.length > 1) warnings.push(`scenario id ${id} is used by ${copies.length} scenarios — give each its own ID before approving`);
37
+ mine = copies.map((c) => ({ id, class: 'proposed', findings: [], file: c.source.file }));
38
+ } else if (copies.length > 1) {
39
+ mine = [{ id, class: 'assertion-lost', file: copies[0].source.file, findings: [finding('assertion-lost',
40
+ `the approved id ${id} is now carried by ${copies.length} scenarios — the lock cannot tell which one QA approved; give each its own ID`)] }];
41
+ } else {
42
+ mine = [classifyScenario(id, entry!, copies[0])];
43
+ }
44
+ results.push(...(copies.some((c) => c.source.outline) ? mine.map(withOutlineFinding) : mine));
45
+ }
46
+ return { results, warnings };
47
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * A free / impl:code scenario's traces against its approved lock entry (DOS-14.5 – 14.6) — the SAME
3
+ * strength ladder as the Gherkin check (DOS-14.4, oracle-strength.ts), fed from the trace fingerprint.
4
+ *
5
+ * free classifyScenario(lock entry, fingerprint): equal hash → match, else the ladder
6
+ * impl:code group by group: each approved step must be realized inside the `test.step` titled
7
+ * with that step — identically (fine), weaker (hard), stronger (strengthened), changed
8
+ * (review); an approved check with no group or no check in its group → assertion-lost
9
+ * overlays a step whose target identity is `low` → opaque (review, never match, red-team #4);
10
+ * an opaque step → opaque with its verbatim text and file:line (contract.opaque-step)
11
+ * runs no admissible trace → unproven (all inconclusive → inconclusive) when APPROVED, a
12
+ * warning while proposed (M5, M6); a trace refused beside an admissible one → a warning;
13
+ * several admissible traces that fingerprint differently → nondeterministic (worst wins)
14
+ */
15
+ import { classifyScenario, ContractFinding, finding, ScenarioResult, severityOf } from './classify-contract-change';
16
+ import { sameJson } from './contract-hash';
17
+ import type { ContractStep, Implementation, LockEntry, ScenarioClass } from './contract-schema';
18
+ import { compareAsserts } from './oracle-strength';
19
+ import type { TraceRun } from './trace/collect-trace-evidence';
20
+ import type { TraceFingerprint } from './trace/trace-fingerprint';
21
+
22
+ /** `warnings` never change the verdict: a not-yet-approved scenario's trace problems, unused traces. */
23
+ export interface TraceOutcome { class: ScenarioClass; findings: ContractFinding[]; hashes: string[]; runs: string[]; warnings: string[] }
24
+
25
+ /** What an unjudgeable scenario becomes: hard when QA locked it (approved), a warning while it is proposed. */
26
+ export function notJudged(id: string, approved: boolean, cls: 'unproven' | 'inconclusive', reasons: string[]): TraceOutcome {
27
+ return approved
28
+ ? { class: cls, findings: reasons.map((r) => finding(cls, r)), hashes: [], runs: [], warnings: [] }
29
+ : { class: 'proposed', findings: [], hashes: [], runs: [], warnings: reasons.map((r) => `${id} (not approved yet, so a warning): ${r}`) };
30
+ }
31
+
32
+ /** How a low target identity opens its finding — `next` reads it to offer `contract check --trace`. */
33
+ export const LOW_IDENTITY_FINDING = 'target identity is low';
34
+
35
+ const worstOf = (findings: ContractFinding[], start: ScenarioClass = 'match'): ScenarioClass =>
36
+ findings.reduce<ScenarioClass>((acc, f) => (severityOf(f.class as ScenarioClass) > severityOf(acc) ? f.class as ScenarioClass : acc), start);
37
+
38
+ function overlays(fp: TraceFingerprint, used?: Set<number>): ContractFinding[] {
39
+ const out: ContractFinding[] = [];
40
+ fp.rendered.forEach((r, i) => {
41
+ if (used && !used.has(i)) return;
42
+ if (r.opaque) out.push(finding('opaque', `"${r.opaque.verbatim}"${r.opaque.where ? ` at ${r.opaque.where}` : ''} — ${r.kind === 'assert' ? 'a check' : 'a step'} sungen cannot compare; QA reviews it`, i + 1));
43
+ else if (r.low) out.push(finding('opaque', `${LOW_IDENTITY_FINDING} (${r.low}) — cannot prove "${r.sentence}" acts on the approved element`, i + 1));
44
+ });
45
+ return out;
46
+ }
47
+
48
+ function implCodeFindings(entry: LockEntry, fp: TraceFingerprint): { findings: ContractFinding[]; used: Set<number> } {
49
+ const findings: ContractFinding[] = [];
50
+ const used = new Set<number>();
51
+ const taken = new Set<number>();
52
+ entry.steps.forEach((locked, i) => {
53
+ const g = fp.groups.findIndex((x, gi) => !taken.has(gi) && sameJson(x.titleStep, locked));
54
+ if (g < 0) {
55
+ findings.push(locked.kind === 'assert'
56
+ ? finding('assertion-lost', `no test.step implements the approved check "${locked.verb}" (step ${i + 1})`)
57
+ : finding('hash-changed', `no test.step implements the approved step "${locked.verb}" (step ${i + 1})`));
58
+ return;
59
+ }
60
+ taken.add(g);
61
+ const members = fp.groups[g].members;
62
+ const same = members.find((m) => sameJson(fp.steps[m], locked));
63
+ if (same !== undefined) { used.add(same); return; }
64
+ const candidates = members.filter((m) => fp.steps[m].kind === locked.kind);
65
+ if (!candidates.length) {
66
+ findings.push(locked.kind === 'assert' ? finding('assertion-lost', `the test.step for "${locked.verb}" checks nothing`, i + 1)
67
+ : finding('hash-changed', `the test.step for "${locked.verb}" does nothing on the page`, i + 1));
68
+ return;
69
+ }
70
+ candidates.forEach((m) => used.add(m));
71
+ if (locked.kind === 'action') { findings.push(finding('hash-changed', `the code for "${locked.verb}" does something else: "${fp.rendered[candidates[0]].sentence}"`, i + 1)); return; }
72
+ const judged = candidates.map((m) => compareAsserts(locked, fp.steps[m] as ContractStep));
73
+ const rank = (k: string): number => (k === 'oracle-weakened' ? 0 : k === 'review' ? 1 : 2);
74
+ const best = judged.sort((a, b) => rank(b.class) - rank(a.class))[0];
75
+ findings.push(finding(best.class === 'review' ? 'hash-changed' : best.class, best.detail, i + 1));
76
+ });
77
+ fp.groups.forEach((g, gi) => { if (!taken.has(gi)) findings.push(finding('hash-changed', `test.step "${g.title}" implements no approved step`)); });
78
+ return { findings, used };
79
+ }
80
+
81
+ function classifyOne(id: string, implementation: Implementation, entry: LockEntry, fp: TraceFingerprint): ContractFinding[] {
82
+ if (implementation === 'impl:code') {
83
+ const { findings, used } = implCodeFindings(entry, fp);
84
+ return [...findings, ...overlays(fp, used)];
85
+ }
86
+ const ladder: ScenarioResult = classifyScenario(id, entry, {
87
+ id, implementation, steps: fp.steps, hash: fp.hash, source: { file: '', name: id, hasId: true, lines: [], outline: false },
88
+ });
89
+ return [...ladder.findings, ...overlays(fp)];
90
+ }
91
+
92
+ const runLabel = (r: TraceRun): string => `${r.record?.runId ?? '?'}#${r.result?.retry ?? '?'}`;
93
+
94
+ export function classifyTraceRuns(
95
+ id: string, implementation: Implementation, entry: LockEntry | undefined, runs: TraceRun[], fingerprints: Map<TraceRun, TraceFingerprint>,
96
+ ): TraceOutcome {
97
+ const admissible = runs.filter((r) => r.status === 'admissible' && fingerprints.has(r));
98
+ const others = runs.filter((r) => !admissible.includes(r));
99
+ const labels = admissible.map(runLabel);
100
+ const hashes = admissible.map((r) => fingerprints.get(r)!.hash);
101
+ if (!admissible.length) {
102
+ const allInconclusive = others.length > 0 && others.every((r) => r.status === 'inconclusive');
103
+ return { ...notJudged(id, !!entry, allInconclusive ? 'inconclusive' : 'unproven', others.map((r) => r.reason ?? r.status)), hashes, runs: labels };
104
+ }
105
+ // A trace that may not speak, beside one that may: the admissible one judges, the other is a warning.
106
+ const warnings = others.map((r) => `${id}: another trace of this scenario was not used — ${r.reason ?? r.status}`);
107
+ if (!entry) return { class: 'proposed', findings: [], hashes, runs: labels, warnings };
108
+ const perRun = admissible.map((r) => classifyOne(id, implementation, entry, fingerprints.get(r)!));
109
+ const findings = perRun.flat();
110
+ let cls = worstOf(perRun.flat());
111
+ if (new Set(hashes).size > 1) {
112
+ findings.push(finding('nondeterministic', `${new Set(hashes).size} different fingerprints across ${admissible.length} runs — the runs took different branches`));
113
+ if (severityOf(cls) <= severityOf('strengthened')) cls = 'nondeterministic';
114
+ }
115
+ return { class: cls, findings, hashes, runs: labels, warnings };
116
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * What git HEAD holds under `qa/`, for the contract (DOS-14.4) — with a timeout the caller picks
3
+ * (≈3 s inside the Claude Code guard, whose own budget is 5 s; 30 s for the CLI).
4
+ *
5
+ * Three answers, never confused:
6
+ * blobs one `git ls-tree -r -z HEAD -- qa`: every committed lock and decision with its blob id
7
+ * no git no `.git` at or above the project, or a repository without a first commit
8
+ * git FAILED `.git` is there but git errors, times out, refuses (dubious ownership) or is not
9
+ * installed — reported as `error`, so the caller can refuse to call a lock "clean"
10
+ *
11
+ * A file whose working bytes hash to its HEAD blob id IS its committed version: it is read from disk
12
+ * and needs no `git show`. git runs with fixed arguments, never through a shell.
13
+ */
14
+ import { execFileSync } from 'child_process';
15
+ import { createHash } from 'crypto';
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+
19
+ export const CLI_GIT_TIMEOUT_MS = 30_000;
20
+ export const GUARD_GIT_TIMEOUT_MS = 3_000;
21
+
22
+ export class GitUnavailableError extends Error {
23
+ constructor(detail: string) {
24
+ super(`git is unavailable in a checkout that has .git (${detail}) — the contract lock cannot be compared with HEAD`);
25
+ this.name = 'GitUnavailableError';
26
+ }
27
+ }
28
+
29
+ /** One flat shape: the project compiles without strictNullChecks, where a boolean-tagged union does not narrow. */
30
+ interface GitRun { ok: boolean; out: string; timedOut: boolean; message: string }
31
+
32
+ function runGit(cwd: string, args: string[], timeoutMs: number): GitRun {
33
+ try {
34
+ const out = execFileSync('git', args, { cwd, stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 64 * 1024 * 1024, timeout: timeoutMs }).toString('utf8');
35
+ return { ok: true, out, timedOut: false, message: '' };
36
+ } catch (e) {
37
+ const err = e as NodeJS.ErrnoException & { signal?: string; stderr?: Buffer };
38
+ const message = (err.stderr?.toString().trim() || err.message || String(e)).split('\n')[0];
39
+ return { ok: false, out: '', timedOut: err.code === 'ETIMEDOUT' || err.signal === 'SIGTERM', message };
40
+ }
41
+ }
42
+
43
+ function hasDotGit(start: string): boolean {
44
+ let dir = path.resolve(start);
45
+ for (let i = 0; i < 40; i++) {
46
+ if (fs.existsSync(path.join(dir, '.git'))) return true;
47
+ const parent = path.dirname(dir);
48
+ if (parent === dir) return false;
49
+ dir = parent;
50
+ }
51
+ return false;
52
+ }
53
+
54
+ export interface HeadSnapshot {
55
+ root: string;
56
+ timeoutMs: number;
57
+ /** Project-relative path → blob id of each committed lock and decision; null when there is nothing to compare with. */
58
+ blobs: Map<string, string> | null;
59
+ /** Set when git FAILED in a checkout that has .git (blobs is then null). */
60
+ error?: string;
61
+ }
62
+
63
+ const RECORD = /^\d+ blob ([0-9a-f]+)\t(.+)$/s;
64
+ const KEEP = (p: string): boolean => p.endsWith('/contracts.lock.yaml') || /\/decisions\/[^/]+\.yaml$/.test(p);
65
+
66
+ export function headSnapshot(root: string, timeoutMs = CLI_GIT_TIMEOUT_MS): HeadSnapshot {
67
+ const r = runGit(root, ['ls-tree', '-r', '-z', 'HEAD', '--', 'qa'], timeoutMs);
68
+ if (r.ok) {
69
+ const blobs = new Map<string, string>();
70
+ for (const rec of r.out.split('\0')) { const m = rec.match(RECORD); if (m && KEEP(m[2])) blobs.set(m[2], m[1]); }
71
+ return { root, timeoutMs, blobs };
72
+ }
73
+ if (r.timedOut) return { root, timeoutMs, blobs: null, error: `git timed out after ${timeoutMs} ms` };
74
+ if (!hasDotGit(root)) return { root, timeoutMs, blobs: null };
75
+ const repo = runGit(root, ['rev-parse', '--git-dir'], timeoutMs);
76
+ if (repo.ok && !runGit(root, ['rev-parse', '--verify', '-q', 'HEAD'], timeoutMs).ok) return { root, timeoutMs, blobs: null }; // no first commit yet
77
+ return { root, timeoutMs, blobs: null, error: r.message || 'git failed' };
78
+ }
79
+
80
+ export const gitBlobId = (text: string): string => {
81
+ const body = Buffer.from(text, 'utf8');
82
+ return createHash('sha1').update(Buffer.concat([Buffer.from(`blob ${body.length}\0`), body])).digest('hex');
83
+ };
84
+
85
+ export const posixRel = (root: string, abs: string): string => path.relative(root, abs).split(path.sep).join('/');
86
+
87
+ /**
88
+ * The committed text of a project-relative path, or null when HEAD does not hold it. `workingText`
89
+ * (the file as it is on disk, if any) spares the `git show` when it is byte-identical to HEAD.
90
+ */
91
+ export function headFileText(snap: HeadSnapshot, rel: string, workingText: string | null): string | null {
92
+ const blob = snap.blobs?.get(rel);
93
+ if (blob === undefined) return null;
94
+ if (workingText !== null && gitBlobId(workingText) === blob) return workingText;
95
+ const r = runGit(snap.root, ['show', `HEAD:./${rel}`], snap.timeoutMs);
96
+ if (!r.ok) throw new GitUnavailableError(`git show HEAD:${rel}: ${r.timedOut ? 'timed out' : r.message}`);
97
+ return r.out;
98
+ }
99
+
100
+ /** A file's text, or null when it does not exist. */
101
+ export function readTextOrNull(file: string): string | null {
102
+ try { return fs.readFileSync(file, 'utf8'); } catch (e) {
103
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') return null;
104
+ throw e;
105
+ }
106
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Contract hash (DOS-14.2): `sha256-12` of the normalized steps PLUS the implementation path, so
3
+ * retagging `@free` → none is a contract change (red-team #5).
4
+ *
5
+ * Canonical JSON is keys-sorted with absent fields dropped and NO other rewriting: a literal's
6
+ * whitespace is part of what the test types or expects, so two values that differ only in spaces
7
+ * hash apart rather than "matching" by normalization.
8
+ */
9
+ import { createHash } from 'crypto';
10
+ import type { ContractStep, Implementation } from './contract-schema';
11
+
12
+ function canonical(v: unknown): unknown {
13
+ if (Array.isArray(v)) return v.map(canonical);
14
+ if (v && typeof v === 'object') {
15
+ const out: Record<string, unknown> = {};
16
+ for (const k of Object.keys(v).sort()) {
17
+ const x = (v as Record<string, unknown>)[k];
18
+ if (x !== undefined) out[k] = canonical(x);
19
+ }
20
+ return out;
21
+ }
22
+ return v;
23
+ }
24
+
25
+ export const canonicalJson = (v: unknown): string => JSON.stringify(canonical(v));
26
+
27
+ /** Structural equality, key order ignored. */
28
+ export const sameJson = (a: unknown, b: unknown): boolean => canonicalJson(a) === canonicalJson(b);
29
+
30
+ export const sha256Hex = (s: string): string => createHash('sha256').update(s).digest('hex');
31
+
32
+ export function contractHash(implementation: Implementation, steps: ContractStep[]): string {
33
+ return sha256Hex(canonicalJson({ implementation, steps })).slice(0, 12);
34
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `.sungen/reports/<slug>-contract.json` — the contract verdict as OUTPUT (DOS-14.0, takumi T3).
3
+ *
4
+ * Written by `sungen contract check` from a verdict it just built; never read back as a source of
5
+ * truth. Anything that needs the verdict calls buildContractVerdict() and rebuilds it from the
6
+ * `.feature`, the lock and git HEAD — so editing this file to "pass" changes nothing, and the next
7
+ * check overwrites it. It records `inputsHash` so a reader can see the report is stale, and
8
+ * `exitCode` next to `verdict` (fail ⇔ exitCode ≠ 0).
9
+ */
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { writeFileAtomic } from '../harness/write-atomic';
13
+ import { reportSlug } from '../harness/unit-paths';
14
+ import type { ContractVerdict } from './build-contract-verdict';
15
+
16
+ export const contractReportPath = (projectRoot: string, unit: string): string =>
17
+ path.join(projectRoot, '.sungen', 'reports', `${reportSlug(unit)}-contract.json`);
18
+
19
+ export function writeContractReport(projectRoot: string, verdict: ContractVerdict): string {
20
+ const file = contractReportPath(projectRoot, verdict.unit);
21
+ fs.mkdirSync(path.dirname(file), { recursive: true });
22
+ const doc = { ...verdict, generatedBy: 'sungen contract check', generatedAt: new Date().toISOString() };
23
+ writeFileAtomic(file, `${JSON.stringify(doc, null, 2)}\n`);
24
+ return file;
25
+ }