@sun-asterisk/sungen 3.2.34-beta.1 → 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 (858) 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 +3 -29
  38. package/dist/cli/commands/audit.js.map +1 -1
  39. package/dist/cli/commands/capability.js +1 -1
  40. package/dist/cli/commands/capability.js.map +1 -1
  41. package/dist/cli/commands/contract-derive-command.d.ts +10 -0
  42. package/dist/cli/commands/contract-derive-command.d.ts.map +1 -0
  43. package/dist/cli/commands/contract-derive-command.js +99 -0
  44. package/dist/cli/commands/contract-derive-command.js.map +1 -0
  45. package/dist/cli/commands/contract.d.ts +17 -0
  46. package/dist/cli/commands/contract.d.ts.map +1 -0
  47. package/dist/cli/commands/contract.js +162 -0
  48. package/dist/cli/commands/contract.js.map +1 -0
  49. package/dist/cli/commands/delivery.d.ts.map +1 -1
  50. package/dist/cli/commands/delivery.js +52 -3
  51. package/dist/cli/commands/delivery.js.map +1 -1
  52. package/dist/cli/commands/generate.d.ts.map +1 -1
  53. package/dist/cli/commands/generate.js +3 -1
  54. package/dist/cli/commands/generate.js.map +1 -1
  55. package/dist/cli/commands/journey.d.ts.map +1 -1
  56. package/dist/cli/commands/journey.js +41 -13
  57. package/dist/cli/commands/journey.js.map +1 -1
  58. package/dist/cli/commands/next.d.ts +8 -0
  59. package/dist/cli/commands/next.d.ts.map +1 -1
  60. package/dist/cli/commands/next.js +64 -6
  61. package/dist/cli/commands/next.js.map +1 -1
  62. package/dist/cli/commands/update.d.ts +11 -0
  63. package/dist/cli/commands/update.d.ts.map +1 -1
  64. package/dist/cli/commands/update.js +31 -6
  65. package/dist/cli/commands/update.js.map +1 -1
  66. package/dist/cli/index.js +20 -35
  67. package/dist/cli/index.js.map +1 -1
  68. package/dist/contract/approve-contract.d.ts +31 -0
  69. package/dist/contract/approve-contract.d.ts.map +1 -0
  70. package/dist/contract/approve-contract.js +87 -0
  71. package/dist/contract/approve-contract.js.map +1 -0
  72. package/dist/contract/assertion-matcher-vocabulary.d.ts +24 -0
  73. package/dist/contract/assertion-matcher-vocabulary.d.ts.map +1 -0
  74. package/dist/contract/assertion-matcher-vocabulary.js +100 -0
  75. package/dist/contract/assertion-matcher-vocabulary.js.map +1 -0
  76. package/dist/contract/build-contract-verdict.d.ts +32 -0
  77. package/dist/contract/build-contract-verdict.d.ts.map +1 -0
  78. package/dist/contract/build-contract-verdict.js +123 -0
  79. package/dist/contract/build-contract-verdict.js.map +1 -0
  80. package/dist/contract/build-trace-conformance.d.ts +18 -0
  81. package/dist/contract/build-trace-conformance.d.ts.map +1 -0
  82. package/dist/contract/build-trace-conformance.js +153 -0
  83. package/dist/contract/build-trace-conformance.js.map +1 -0
  84. package/dist/contract/classify-contract-change.d.ts +23 -0
  85. package/dist/contract/classify-contract-change.d.ts.map +1 -0
  86. package/dist/contract/classify-contract-change.js +98 -0
  87. package/dist/contract/classify-contract-change.js.map +1 -0
  88. package/dist/contract/classify-contract-unit.d.ts +18 -0
  89. package/dist/contract/classify-contract-unit.d.ts.map +1 -0
  90. package/dist/contract/classify-contract-unit.js +49 -0
  91. package/dist/contract/classify-contract-unit.js.map +1 -0
  92. package/dist/contract/classify-trace-conformance.d.ts +32 -0
  93. package/dist/contract/classify-trace-conformance.d.ts.map +1 -0
  94. package/dist/contract/classify-trace-conformance.js +117 -0
  95. package/dist/contract/classify-trace-conformance.js.map +1 -0
  96. package/dist/contract/contract-git.d.ts +24 -0
  97. package/dist/contract/contract-git.d.ts.map +1 -0
  98. package/dist/contract/contract-git.js +144 -0
  99. package/dist/contract/contract-git.js.map +1 -0
  100. package/dist/contract/contract-hash.d.ts +7 -0
  101. package/dist/contract/contract-hash.d.ts.map +1 -0
  102. package/dist/contract/contract-hash.js +38 -0
  103. package/dist/contract/contract-hash.js.map +1 -0
  104. package/dist/contract/contract-report-file.d.ts +4 -0
  105. package/dist/contract/contract-report-file.d.ts.map +1 -0
  106. package/dist/contract/contract-report-file.js +60 -0
  107. package/dist/contract/contract-report-file.js.map +1 -0
  108. package/dist/contract/contract-schema.d.ts +71 -0
  109. package/dist/contract/contract-schema.d.ts.map +1 -0
  110. package/dist/contract/contract-schema.js +102 -0
  111. package/dist/contract/contract-schema.js.map +1 -0
  112. package/dist/contract/contract-units.d.ts +12 -0
  113. package/dist/contract/contract-units.d.ts.map +1 -0
  114. package/dist/contract/contract-units.js +96 -0
  115. package/dist/contract/contract-units.js.map +1 -0
  116. package/dist/contract/contracts-lock-file.d.ts +24 -0
  117. package/dist/contract/contracts-lock-file.d.ts.map +1 -0
  118. package/dist/contract/contracts-lock-file.js +140 -0
  119. package/dist/contract/contracts-lock-file.js.map +1 -0
  120. package/dist/contract/data-normalize.d.ts +10 -0
  121. package/dist/contract/data-normalize.d.ts.map +1 -0
  122. package/dist/contract/data-normalize.js +110 -0
  123. package/dist/contract/data-normalize.js.map +1 -0
  124. package/dist/contract/derive-free-feature.d.ts +30 -0
  125. package/dist/contract/derive-free-feature.d.ts.map +1 -0
  126. package/dist/contract/derive-free-feature.js +145 -0
  127. package/dist/contract/derive-free-feature.js.map +1 -0
  128. package/dist/contract/field-semantic-redaction.d.ts +21 -0
  129. package/dist/contract/field-semantic-redaction.d.ts.map +1 -0
  130. package/dist/contract/field-semantic-redaction.js +118 -0
  131. package/dist/contract/field-semantic-redaction.js.map +1 -0
  132. package/dist/contract/free-feature-blocks.d.ts +10 -0
  133. package/dist/contract/free-feature-blocks.d.ts.map +1 -0
  134. package/dist/contract/free-feature-blocks.js +94 -0
  135. package/dist/contract/free-feature-blocks.js.map +1 -0
  136. package/dist/contract/lock-secret-guard.d.ts +22 -0
  137. package/dist/contract/lock-secret-guard.d.ts.map +1 -0
  138. package/dist/contract/lock-secret-guard.js +134 -0
  139. package/dist/contract/lock-secret-guard.js.map +1 -0
  140. package/dist/contract/lock-vs-git-head.d.ts +26 -0
  141. package/dist/contract/lock-vs-git-head.d.ts.map +1 -0
  142. package/dist/contract/lock-vs-git-head.js +28 -0
  143. package/dist/contract/lock-vs-git-head.js.map +1 -0
  144. package/dist/contract/normalize-contract-step.d.ts +21 -0
  145. package/dist/contract/normalize-contract-step.d.ts.map +1 -0
  146. package/dist/contract/normalize-contract-step.js +70 -0
  147. package/dist/contract/normalize-contract-step.js.map +1 -0
  148. package/dist/contract/normalize-feature-steps.d.ts +7 -0
  149. package/dist/contract/normalize-feature-steps.d.ts.map +1 -0
  150. package/dist/contract/normalize-feature-steps.js +127 -0
  151. package/dist/contract/normalize-feature-steps.js.map +1 -0
  152. package/dist/contract/oracle-strength.d.ts +12 -0
  153. package/dist/contract/oracle-strength.d.ts.map +1 -0
  154. package/dist/contract/oracle-strength.js +88 -0
  155. package/dist/contract/oracle-strength.js.map +1 -0
  156. package/dist/contract/project-contract-blockers.d.ts +10 -0
  157. package/dist/contract/project-contract-blockers.d.ts.map +1 -0
  158. package/dist/contract/project-contract-blockers.js +73 -0
  159. package/dist/contract/project-contract-blockers.js.map +1 -0
  160. package/dist/contract/record-contract-approval.d.ts +21 -0
  161. package/dist/contract/record-contract-approval.d.ts.map +1 -0
  162. package/dist/contract/record-contract-approval.js +98 -0
  163. package/dist/contract/record-contract-approval.js.map +1 -0
  164. package/dist/contract/render-free-gherkin.d.ts +40 -0
  165. package/dist/contract/render-free-gherkin.d.ts.map +1 -0
  166. package/dist/contract/render-free-gherkin.js +141 -0
  167. package/dist/contract/render-free-gherkin.js.map +1 -0
  168. package/dist/contract/render-trace-sentence.d.ts +20 -0
  169. package/dist/contract/render-trace-sentence.d.ts.map +1 -0
  170. package/dist/contract/render-trace-sentence.js +109 -0
  171. package/dist/contract/render-trace-sentence.js.map +1 -0
  172. package/dist/contract/render-trace-target.d.ts +14 -0
  173. package/dist/contract/render-trace-target.d.ts.map +1 -0
  174. package/dist/contract/render-trace-target.js +70 -0
  175. package/dist/contract/render-trace-target.js.map +1 -0
  176. package/dist/contract/resolve-unit-trace-identities.d.ts +8 -0
  177. package/dist/contract/resolve-unit-trace-identities.d.ts.map +1 -0
  178. package/dist/contract/resolve-unit-trace-identities.js +78 -0
  179. package/dist/contract/resolve-unit-trace-identities.js.map +1 -0
  180. package/dist/contract/trace/collect-trace-evidence.d.ts +33 -0
  181. package/dist/contract/trace/collect-trace-evidence.d.ts.map +1 -0
  182. package/dist/contract/trace/collect-trace-evidence.js +161 -0
  183. package/dist/contract/trace/collect-trace-evidence.js.map +1 -0
  184. package/dist/contract/trace/identity-cache.d.ts +19 -0
  185. package/dist/contract/trace/identity-cache.d.ts.map +1 -0
  186. package/dist/contract/trace/identity-cache.js +104 -0
  187. package/dist/contract/trace/identity-cache.js.map +1 -0
  188. package/dist/contract/trace/select-final-attempt.d.ts +27 -0
  189. package/dist/contract/trace/select-final-attempt.d.ts.map +1 -0
  190. package/dist/contract/trace/select-final-attempt.js +116 -0
  191. package/dist/contract/trace/select-final-attempt.js.map +1 -0
  192. package/dist/contract/trace/snapshot-html-renderer.d.ts +31 -0
  193. package/dist/contract/trace/snapshot-html-renderer.d.ts.map +1 -0
  194. package/dist/contract/trace/snapshot-html-renderer.js +172 -0
  195. package/dist/contract/trace/snapshot-html-renderer.js.map +1 -0
  196. package/dist/contract/trace/snapshot-resolver.d.ts +21 -0
  197. package/dist/contract/trace/snapshot-resolver.d.ts.map +1 -0
  198. package/dist/contract/trace/snapshot-resolver.js +65 -0
  199. package/dist/contract/trace/snapshot-resolver.js.map +1 -0
  200. package/dist/contract/trace/snapshot-store.d.ts +66 -0
  201. package/dist/contract/trace/snapshot-store.d.ts.map +1 -0
  202. package/dist/contract/trace/snapshot-store.js +173 -0
  203. package/dist/contract/trace/snapshot-store.js.map +1 -0
  204. package/dist/contract/trace/target-identity.d.ts +74 -0
  205. package/dist/contract/trace/target-identity.d.ts.map +1 -0
  206. package/dist/contract/trace/target-identity.js +68 -0
  207. package/dist/contract/trace/target-identity.js.map +1 -0
  208. package/dist/contract/trace/test-step-join.d.ts +65 -0
  209. package/dist/contract/trace/test-step-join.d.ts.map +1 -0
  210. package/dist/contract/trace/test-step-join.js +80 -0
  211. package/dist/contract/trace/test-step-join.js.map +1 -0
  212. package/dist/contract/trace/trace-binding.d.ts +21 -0
  213. package/dist/contract/trace/trace-binding.d.ts.map +1 -0
  214. package/dist/contract/trace/trace-binding.js +88 -0
  215. package/dist/contract/trace/trace-binding.js.map +1 -0
  216. package/dist/contract/trace/trace-file-reader.d.ts +35 -0
  217. package/dist/contract/trace/trace-file-reader.d.ts.map +1 -0
  218. package/dist/contract/trace/trace-file-reader.js +155 -0
  219. package/dist/contract/trace/trace-file-reader.js.map +1 -0
  220. package/dist/contract/trace/trace-fingerprint.d.ts +22 -0
  221. package/dist/contract/trace/trace-fingerprint.d.ts.map +1 -0
  222. package/dist/contract/trace/trace-fingerprint.js +96 -0
  223. package/dist/contract/trace/trace-fingerprint.js.map +1 -0
  224. package/dist/contract/trace/trace-identity-resolution.d.ts +26 -0
  225. package/dist/contract/trace/trace-identity-resolution.d.ts.map +1 -0
  226. package/dist/contract/trace/trace-identity-resolution.js +84 -0
  227. package/dist/contract/trace/trace-identity-resolution.js.map +1 -0
  228. package/dist/contract/trace/trace-run-record.d.ts +27 -0
  229. package/dist/contract/trace/trace-run-record.d.ts.map +1 -0
  230. package/dist/contract/trace/trace-run-record.js +115 -0
  231. package/dist/contract/trace/trace-run-record.js.map +1 -0
  232. package/dist/contract/trace/trace-steps.d.ts +19 -0
  233. package/dist/contract/trace/trace-steps.d.ts.map +1 -0
  234. package/dist/contract/trace/trace-steps.js +79 -0
  235. package/dist/contract/trace/trace-steps.js.map +1 -0
  236. package/dist/contract/trace/trace-zip-limits.d.ts +41 -0
  237. package/dist/contract/trace/trace-zip-limits.d.ts.map +1 -0
  238. package/dist/contract/trace/trace-zip-limits.js +48 -0
  239. package/dist/contract/trace/trace-zip-limits.js.map +1 -0
  240. package/dist/contract/trace/trace-zip-reader.d.ts +10 -0
  241. package/dist/contract/trace/trace-zip-reader.d.ts.map +1 -0
  242. package/dist/contract/trace/trace-zip-reader.js +186 -0
  243. package/dist/contract/trace/trace-zip-reader.js.map +1 -0
  244. package/dist/contract/verify-approval-decisions.d.ts +5 -0
  245. package/dist/contract/verify-approval-decisions.d.ts.map +1 -0
  246. package/dist/contract/verify-approval-decisions.js +104 -0
  247. package/dist/contract/verify-approval-decisions.js.map +1 -0
  248. package/dist/decisions/append-decision.d.ts +12 -0
  249. package/dist/decisions/append-decision.d.ts.map +1 -0
  250. package/dist/decisions/append-decision.js +73 -0
  251. package/dist/decisions/append-decision.js.map +1 -0
  252. package/dist/decisions/confirmed-decision-commands.d.ts +30 -0
  253. package/dist/decisions/confirmed-decision-commands.d.ts.map +1 -0
  254. package/dist/decisions/confirmed-decision-commands.js +172 -0
  255. package/dist/decisions/confirmed-decision-commands.js.map +1 -0
  256. package/dist/decisions/decision-file.d.ts +31 -0
  257. package/dist/decisions/decision-file.d.ts.map +1 -0
  258. package/dist/decisions/decision-file.js +139 -0
  259. package/dist/decisions/decision-file.js.map +1 -0
  260. package/dist/decisions/decision-identity.d.ts +12 -0
  261. package/dist/decisions/decision-identity.d.ts.map +1 -0
  262. package/dist/decisions/decision-identity.js +78 -0
  263. package/dist/decisions/decision-identity.js.map +1 -0
  264. package/dist/decisions/decision-integrity-check.d.ts +12 -0
  265. package/dist/decisions/decision-integrity-check.d.ts.map +1 -0
  266. package/dist/decisions/decision-integrity-check.js +102 -0
  267. package/dist/decisions/decision-integrity-check.js.map +1 -0
  268. package/dist/decisions/decision-schema.d.ts +42 -0
  269. package/dist/decisions/decision-schema.d.ts.map +1 -0
  270. package/dist/decisions/decision-schema.js +75 -0
  271. package/dist/decisions/decision-schema.js.map +1 -0
  272. package/dist/decisions/decision-source.d.ts +2 -0
  273. package/dist/decisions/decision-source.d.ts.map +1 -0
  274. package/dist/decisions/decision-source.js +18 -0
  275. package/dist/decisions/decision-source.js.map +1 -0
  276. package/dist/decisions/exclusive-file-create.d.ts +3 -0
  277. package/dist/decisions/exclusive-file-create.d.ts.map +1 -0
  278. package/dist/decisions/exclusive-file-create.js +84 -0
  279. package/dist/decisions/exclusive-file-create.js.map +1 -0
  280. package/dist/decisions/git-head-trust.d.ts +7 -0
  281. package/dist/decisions/git-head-trust.d.ts.map +1 -0
  282. package/dist/decisions/git-head-trust.js +76 -0
  283. package/dist/decisions/git-head-trust.js.map +1 -0
  284. package/dist/decisions/human-confirmation.d.ts +33 -0
  285. package/dist/decisions/human-confirmation.d.ts.map +1 -0
  286. package/dist/decisions/human-confirmation.js +155 -0
  287. package/dist/decisions/human-confirmation.js.map +1 -0
  288. package/dist/decisions/legacy-waiver-migration.d.ts +31 -0
  289. package/dist/decisions/legacy-waiver-migration.d.ts.map +1 -0
  290. package/dist/decisions/legacy-waiver-migration.js +170 -0
  291. package/dist/decisions/legacy-waiver-migration.js.map +1 -0
  292. package/dist/decisions/read-decisions.d.ts +19 -0
  293. package/dist/decisions/read-decisions.d.ts.map +1 -0
  294. package/dist/decisions/read-decisions.js +90 -0
  295. package/dist/decisions/read-decisions.js.map +1 -0
  296. package/dist/decisions/viewpoint-baseline-adapter.d.ts +7 -0
  297. package/dist/decisions/viewpoint-baseline-adapter.d.ts.map +1 -0
  298. package/dist/decisions/viewpoint-baseline-adapter.js +109 -0
  299. package/dist/decisions/viewpoint-baseline-adapter.js.map +1 -0
  300. package/dist/decisions/waiver-decisions.d.ts +24 -0
  301. package/dist/decisions/waiver-decisions.d.ts.map +1 -0
  302. package/dist/decisions/waiver-decisions.js +46 -0
  303. package/dist/decisions/waiver-decisions.js.map +1 -0
  304. package/dist/exporters/matrix/build.d.ts +3 -0
  305. package/dist/exporters/matrix/build.d.ts.map +1 -1
  306. package/dist/exporters/matrix/build.js +10 -9
  307. package/dist/exporters/matrix/build.js.map +1 -1
  308. package/dist/exporters/matrix/export.d.ts +3 -0
  309. package/dist/exporters/matrix/export.d.ts.map +1 -1
  310. package/dist/exporters/matrix/export.js +2 -0
  311. package/dist/exporters/matrix/export.js.map +1 -1
  312. package/dist/exporters/matrix/gates.d.ts +6 -1
  313. package/dist/exporters/matrix/gates.d.ts.map +1 -1
  314. package/dist/exporters/matrix/gates.js +57 -128
  315. package/dist/exporters/matrix/gates.js.map +1 -1
  316. package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
  317. package/dist/exporters/matrix/render-csv.js +3 -0
  318. package/dist/exporters/matrix/render-csv.js.map +1 -1
  319. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts +13 -0
  320. package/dist/exporters/matrix/render-gate-overrides-sheet.d.ts.map +1 -0
  321. package/dist/exporters/matrix/render-gate-overrides-sheet.js +44 -0
  322. package/dist/exporters/matrix/render-gate-overrides-sheet.js.map +1 -0
  323. package/dist/exporters/matrix/render-xlsx.d.ts +1 -0
  324. package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
  325. package/dist/exporters/matrix/render-xlsx.js +3 -0
  326. package/dist/exporters/matrix/render-xlsx.js.map +1 -1
  327. package/dist/exporters/matrix/types.d.ts +4 -1
  328. package/dist/exporters/matrix/types.d.ts.map +1 -1
  329. package/dist/exporters/matrix/types.js.map +1 -1
  330. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  331. package/dist/generators/test-generator/code-generator.js +13 -0
  332. package/dist/generators/test-generator/code-generator.js.map +1 -1
  333. package/dist/graph/evaluate-graph.d.ts +21 -0
  334. package/dist/graph/evaluate-graph.d.ts.map +1 -0
  335. package/dist/graph/evaluate-graph.js +49 -0
  336. package/dist/graph/evaluate-graph.js.map +1 -0
  337. package/dist/graph/locate-finding.d.ts +20 -0
  338. package/dist/graph/locate-finding.d.ts.map +1 -0
  339. package/dist/graph/locate-finding.js +137 -0
  340. package/dist/graph/locate-finding.js.map +1 -0
  341. package/dist/graph/node-decl.d.ts +59 -0
  342. package/dist/graph/node-decl.d.ts.map +1 -0
  343. package/dist/graph/node-decl.js +8 -0
  344. package/dist/graph/node-decl.js.map +1 -0
  345. package/dist/graph/nodes/core-node-catalog.d.ts +7 -0
  346. package/dist/graph/nodes/core-node-catalog.d.ts.map +1 -0
  347. package/dist/graph/nodes/core-node-catalog.js +13 -0
  348. package/dist/graph/nodes/core-node-catalog.js.map +1 -0
  349. package/dist/graph/nodes/test-case-contract-nodes.d.ts +19 -0
  350. package/dist/graph/nodes/test-case-contract-nodes.d.ts.map +1 -0
  351. package/dist/graph/nodes/test-case-contract-nodes.js +56 -0
  352. package/dist/graph/nodes/test-case-contract-nodes.js.map +1 -0
  353. package/dist/graph/nodes/test-case-nodes.d.ts +26 -0
  354. package/dist/graph/nodes/test-case-nodes.d.ts.map +1 -0
  355. package/dist/graph/nodes/test-case-nodes.js +66 -0
  356. package/dist/graph/nodes/test-case-nodes.js.map +1 -0
  357. package/dist/graph/nodes/unit-audit-nodes.d.ts +18 -0
  358. package/dist/graph/nodes/unit-audit-nodes.d.ts.map +1 -0
  359. package/dist/graph/nodes/unit-audit-nodes.js +95 -0
  360. package/dist/graph/nodes/unit-audit-nodes.js.map +1 -0
  361. package/dist/graph/nodes/unit-contract-node.d.ts +20 -0
  362. package/dist/graph/nodes/unit-contract-node.d.ts.map +1 -0
  363. package/dist/graph/nodes/unit-contract-node.js +81 -0
  364. package/dist/graph/nodes/unit-contract-node.js.map +1 -0
  365. package/dist/graph/nodes/unit-design-nodes.d.ts +14 -0
  366. package/dist/graph/nodes/unit-design-nodes.d.ts.map +1 -0
  367. package/dist/graph/nodes/unit-design-nodes.js +90 -0
  368. package/dist/graph/nodes/unit-design-nodes.js.map +1 -0
  369. package/dist/graph/nodes/unit-execution-nodes.d.ts +16 -0
  370. package/dist/graph/nodes/unit-execution-nodes.d.ts.map +1 -0
  371. package/dist/graph/nodes/unit-execution-nodes.js +79 -0
  372. package/dist/graph/nodes/unit-execution-nodes.js.map +1 -0
  373. package/dist/graph/test-case-identity.d.ts +13 -0
  374. package/dist/graph/test-case-identity.d.ts.map +1 -0
  375. package/dist/graph/test-case-identity.js +106 -0
  376. package/dist/graph/test-case-identity.js.map +1 -0
  377. package/dist/graph/test-case-results.d.ts +11 -0
  378. package/dist/graph/test-case-results.d.ts.map +1 -0
  379. package/dist/graph/test-case-results.js +137 -0
  380. package/dist/graph/test-case-results.js.map +1 -0
  381. package/dist/graph/test-case-satisfied.d.ts +25 -0
  382. package/dist/graph/test-case-satisfied.d.ts.map +1 -0
  383. package/dist/graph/test-case-satisfied.js +51 -0
  384. package/dist/graph/test-case-satisfied.js.map +1 -0
  385. package/dist/graph/test-case-state-shape.d.ts +67 -0
  386. package/dist/graph/test-case-state-shape.d.ts.map +1 -0
  387. package/dist/graph/test-case-state-shape.js +3 -0
  388. package/dist/graph/test-case-state-shape.js.map +1 -0
  389. package/dist/graph/test-case-states.d.ts +9 -0
  390. package/dist/graph/test-case-states.d.ts.map +1 -0
  391. package/dist/graph/test-case-states.js +116 -0
  392. package/dist/graph/test-case-states.js.map +1 -0
  393. package/dist/graph/unit-artifact-probes.d.ts +19 -0
  394. package/dist/graph/unit-artifact-probes.d.ts.map +1 -0
  395. package/dist/graph/unit-artifact-probes.js +133 -0
  396. package/dist/graph/unit-artifact-probes.js.map +1 -0
  397. package/dist/graph/unit-state-facts.d.ts +43 -0
  398. package/dist/graph/unit-state-facts.d.ts.map +1 -0
  399. package/dist/graph/unit-state-facts.js +87 -0
  400. package/dist/graph/unit-state-facts.js.map +1 -0
  401. package/dist/harness/audit.d.ts +11 -0
  402. package/dist/harness/audit.d.ts.map +1 -1
  403. package/dist/harness/audit.js +18 -17
  404. package/dist/harness/audit.js.map +1 -1
  405. package/dist/harness/catalog/gate-rules.yaml +647 -0
  406. package/dist/harness/gates/audit-gate-terms.d.ts +41 -0
  407. package/dist/harness/gates/audit-gate-terms.d.ts.map +1 -0
  408. package/dist/harness/gates/audit-gate-terms.js +72 -0
  409. package/dist/harness/gates/audit-gate-terms.js.map +1 -0
  410. package/dist/harness/gates/audit-gate-verdict.d.ts +31 -0
  411. package/dist/harness/gates/audit-gate-verdict.d.ts.map +1 -0
  412. package/dist/harness/gates/audit-gate-verdict.js +101 -0
  413. package/dist/harness/gates/audit-gate-verdict.js.map +1 -0
  414. package/dist/harness/gates/audit-input-hash.d.ts +4 -0
  415. package/dist/harness/gates/audit-input-hash.d.ts.map +1 -0
  416. package/dist/harness/gates/audit-input-hash.js +96 -0
  417. package/dist/harness/gates/audit-input-hash.js.map +1 -0
  418. package/dist/harness/gates/audit-report-verification.d.ts +32 -0
  419. package/dist/harness/gates/audit-report-verification.d.ts.map +1 -0
  420. package/dist/harness/gates/audit-report-verification.js +207 -0
  421. package/dist/harness/gates/audit-report-verification.js.map +1 -0
  422. package/dist/harness/gates/decisions-hash.d.ts +13 -0
  423. package/dist/harness/gates/decisions-hash.d.ts.map +1 -0
  424. package/dist/harness/gates/decisions-hash.js +72 -0
  425. package/dist/harness/gates/decisions-hash.js.map +1 -0
  426. package/dist/harness/gates/delivery-checklist.d.ts +21 -0
  427. package/dist/harness/gates/delivery-checklist.d.ts.map +1 -0
  428. package/dist/harness/gates/delivery-checklist.js +128 -0
  429. package/dist/harness/gates/delivery-checklist.js.map +1 -0
  430. package/dist/harness/gates/delivery-decision-gate.d.ts +29 -0
  431. package/dist/harness/gates/delivery-decision-gate.d.ts.map +1 -0
  432. package/dist/harness/gates/delivery-decision-gate.js +91 -0
  433. package/dist/harness/gates/delivery-decision-gate.js.map +1 -0
  434. package/dist/harness/gates/delivery-decision-step.d.ts +16 -0
  435. package/dist/harness/gates/delivery-decision-step.d.ts.map +1 -0
  436. package/dist/harness/gates/delivery-decision-step.js +71 -0
  437. package/dist/harness/gates/delivery-decision-step.js.map +1 -0
  438. package/dist/harness/gates/gate-override-report.d.ts +59 -0
  439. package/dist/harness/gates/gate-override-report.d.ts.map +1 -0
  440. package/dist/harness/gates/gate-override-report.js +86 -0
  441. package/dist/harness/gates/gate-override-report.js.map +1 -0
  442. package/dist/harness/gates/gate-override-scope.d.ts +18 -0
  443. package/dist/harness/gates/gate-override-scope.d.ts.map +1 -0
  444. package/dist/harness/gates/gate-override-scope.js +98 -0
  445. package/dist/harness/gates/gate-override-scope.js.map +1 -0
  446. package/dist/harness/gates/gate-override-validation.d.ts +25 -0
  447. package/dist/harness/gates/gate-override-validation.d.ts.map +1 -0
  448. package/dist/harness/gates/gate-override-validation.js +81 -0
  449. package/dist/harness/gates/gate-override-validation.js.map +1 -0
  450. package/dist/harness/gates/gate-rule-catalog.d.ts +24 -0
  451. package/dist/harness/gates/gate-rule-catalog.d.ts.map +1 -0
  452. package/dist/harness/gates/gate-rule-catalog.js +112 -0
  453. package/dist/harness/gates/gate-rule-catalog.js.map +1 -0
  454. package/dist/harness/gates/gate-terms.d.ts +33 -0
  455. package/dist/harness/gates/gate-terms.d.ts.map +1 -0
  456. package/dist/harness/gates/gate-terms.js +41 -0
  457. package/dist/harness/gates/gate-terms.js.map +1 -0
  458. package/dist/harness/gates/in-run-decision-check.d.ts +21 -0
  459. package/dist/harness/gates/in-run-decision-check.d.ts.map +1 -0
  460. package/dist/harness/gates/in-run-decision-check.js +138 -0
  461. package/dist/harness/gates/in-run-decision-check.js.map +1 -0
  462. package/dist/harness/gates/journey-gate-inputs.d.ts +37 -0
  463. package/dist/harness/gates/journey-gate-inputs.d.ts.map +1 -0
  464. package/dist/harness/gates/journey-gate-inputs.js +81 -0
  465. package/dist/harness/gates/journey-gate-inputs.js.map +1 -0
  466. package/dist/harness/gates/migrated-waiver-review.d.ts +6 -0
  467. package/dist/harness/gates/migrated-waiver-review.d.ts.map +1 -0
  468. package/dist/harness/gates/migrated-waiver-review.js +116 -0
  469. package/dist/harness/gates/migrated-waiver-review.js.map +1 -0
  470. package/dist/harness/gates/resolve-rule.d.ts +59 -0
  471. package/dist/harness/gates/resolve-rule.d.ts.map +1 -0
  472. package/dist/harness/gates/resolve-rule.js +41 -0
  473. package/dist/harness/gates/resolve-rule.js.map +1 -0
  474. package/dist/harness/gates/unit-gate-overrides.d.ts +25 -0
  475. package/dist/harness/gates/unit-gate-overrides.d.ts.map +1 -0
  476. package/dist/harness/gates/unit-gate-overrides.js +122 -0
  477. package/dist/harness/gates/unit-gate-overrides.js.map +1 -0
  478. package/dist/harness/journey.d.ts +24 -1
  479. package/dist/harness/journey.d.ts.map +1 -1
  480. package/dist/harness/journey.js +114 -23
  481. package/dist/harness/journey.js.map +1 -1
  482. package/dist/harness/manifest.d.ts +12 -0
  483. package/dist/harness/manifest.d.ts.map +1 -1
  484. package/dist/harness/manifest.js +71 -43
  485. package/dist/harness/manifest.js.map +1 -1
  486. package/dist/harness/next-step.d.ts +39 -6
  487. package/dist/harness/next-step.d.ts.map +1 -1
  488. package/dist/harness/next-step.js +34 -218
  489. package/dist/harness/next-step.js.map +1 -1
  490. package/dist/harness/parse.d.ts +10 -6
  491. package/dist/harness/parse.d.ts.map +1 -1
  492. package/dist/harness/parse.js +22 -11
  493. package/dist/harness/parse.js.map +1 -1
  494. package/dist/harness/script-check.d.ts.map +1 -1
  495. package/dist/harness/script-check.js +22 -5
  496. package/dist/harness/script-check.js.map +1 -1
  497. package/dist/harness/sensors.d.ts +14 -3
  498. package/dist/harness/sensors.d.ts.map +1 -1
  499. package/dist/harness/sensors.js +50 -49
  500. package/dist/harness/sensors.js.map +1 -1
  501. package/dist/harness/viewpoint-baseline.d.ts.map +1 -1
  502. package/dist/harness/viewpoint-baseline.js +1 -7
  503. package/dist/harness/viewpoint-baseline.js.map +1 -1
  504. package/dist/harness/viewpoint-ledger.d.ts +3 -13
  505. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  506. package/dist/harness/viewpoint-ledger.js +8 -38
  507. package/dist/harness/viewpoint-ledger.js.map +1 -1
  508. package/dist/hooks/bash-write-targets.d.ts +15 -0
  509. package/dist/hooks/bash-write-targets.d.ts.map +1 -0
  510. package/dist/hooks/bash-write-targets.js +173 -0
  511. package/dist/hooks/bash-write-targets.js.map +1 -0
  512. package/dist/hooks/claude-hook-entry.d.ts +11 -0
  513. package/dist/hooks/claude-hook-entry.d.ts.map +1 -0
  514. package/dist/hooks/claude-hook-entry.js +137 -0
  515. package/dist/hooks/claude-hook-entry.js.map +1 -0
  516. package/dist/hooks/claude-transcript-turn.d.ts +11 -0
  517. package/dist/hooks/claude-transcript-turn.d.ts.map +1 -0
  518. package/dist/hooks/claude-transcript-turn.js +143 -0
  519. package/dist/hooks/claude-transcript-turn.js.map +1 -0
  520. package/dist/hooks/guard-delivery-contract-rule.d.ts +10 -0
  521. package/dist/hooks/guard-delivery-contract-rule.d.ts.map +1 -0
  522. package/dist/hooks/guard-delivery-contract-rule.js +152 -0
  523. package/dist/hooks/guard-delivery-contract-rule.js.map +1 -0
  524. package/dist/hooks/guard-protected-paths.d.ts +9 -0
  525. package/dist/hooks/guard-protected-paths.d.ts.map +1 -0
  526. package/dist/hooks/guard-protected-paths.js +106 -0
  527. package/dist/hooks/guard-protected-paths.js.map +1 -0
  528. package/dist/hooks/guard-rules.d.ts +37 -0
  529. package/dist/hooks/guard-rules.d.ts.map +1 -0
  530. package/dist/hooks/guard-rules.js +61 -0
  531. package/dist/hooks/guard-rules.js.map +1 -0
  532. package/dist/hooks/guard-tty-spoof-rule.d.ts +15 -0
  533. package/dist/hooks/guard-tty-spoof-rule.d.ts.map +1 -0
  534. package/dist/hooks/guard-tty-spoof-rule.js +21 -0
  535. package/dist/hooks/guard-tty-spoof-rule.js.map +1 -0
  536. package/dist/hooks/hand-back-core.d.ts +37 -0
  537. package/dist/hooks/hand-back-core.d.ts.map +1 -0
  538. package/dist/hooks/hand-back-core.js +115 -0
  539. package/dist/hooks/hand-back-core.js.map +1 -0
  540. package/dist/hooks/hook-cli-command.d.ts +9 -0
  541. package/dist/hooks/hook-cli-command.d.ts.map +1 -0
  542. package/dist/hooks/hook-cli-command.js +46 -0
  543. package/dist/hooks/hook-cli-command.js.map +1 -0
  544. package/dist/hooks/hook-registry.d.ts +10 -0
  545. package/dist/hooks/hook-registry.d.ts.map +1 -0
  546. package/dist/hooks/hook-registry.js +24 -0
  547. package/dist/hooks/hook-registry.js.map +1 -0
  548. package/dist/hooks/hook-result-contract.d.ts +50 -0
  549. package/dist/hooks/hook-result-contract.d.ts.map +1 -0
  550. package/dist/hooks/hook-result-contract.js +80 -0
  551. package/dist/hooks/hook-result-contract.js.map +1 -0
  552. package/dist/hooks/hook-toggles.d.ts +10 -0
  553. package/dist/hooks/hook-toggles.d.ts.map +1 -0
  554. package/dist/hooks/hook-toggles.js +107 -0
  555. package/dist/hooks/hook-toggles.js.map +1 -0
  556. package/dist/hooks/pre-tool-guard-hook.d.ts +10 -0
  557. package/dist/hooks/pre-tool-guard-hook.d.ts.map +1 -0
  558. package/dist/hooks/pre-tool-guard-hook.js +105 -0
  559. package/dist/hooks/pre-tool-guard-hook.js.map +1 -0
  560. package/dist/hooks/session-start-unit-state-hook.d.ts +13 -0
  561. package/dist/hooks/session-start-unit-state-hook.d.ts.map +1 -0
  562. package/dist/hooks/session-start-unit-state-hook.js +55 -0
  563. package/dist/hooks/session-start-unit-state-hook.js.map +1 -0
  564. package/dist/hooks/stop-next-step-hook.d.ts +16 -0
  565. package/dist/hooks/stop-next-step-hook.d.ts.map +1 -0
  566. package/dist/hooks/stop-next-step-hook.js +42 -0
  567. package/dist/hooks/stop-next-step-hook.js.map +1 -0
  568. package/dist/hooks/sungen-project-root.d.ts +19 -0
  569. package/dist/hooks/sungen-project-root.d.ts.map +1 -0
  570. package/dist/hooks/sungen-project-root.js +130 -0
  571. package/dist/hooks/sungen-project-root.js.map +1 -0
  572. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts +15 -0
  573. package/dist/orchestrator/ai-skills/command-partial-includes.d.ts.map +1 -0
  574. package/dist/orchestrator/ai-skills/command-partial-includes.js +106 -0
  575. package/dist/orchestrator/ai-skills/command-partial-includes.js.map +1 -0
  576. package/dist/orchestrator/ai-skills/commands.d.ts +5 -1
  577. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
  578. package/dist/orchestrator/ai-skills/commands.js +16 -6
  579. package/dist/orchestrator/ai-skills/commands.js.map +1 -1
  580. package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
  581. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -1
  582. package/dist/orchestrator/claude-hooks-install.d.ts +27 -0
  583. package/dist/orchestrator/claude-hooks-install.d.ts.map +1 -0
  584. package/dist/orchestrator/claude-hooks-install.js +203 -0
  585. package/dist/orchestrator/claude-hooks-install.js.map +1 -0
  586. package/dist/orchestrator/flow-manager.d.ts +1 -0
  587. package/dist/orchestrator/flow-manager.d.ts.map +1 -1
  588. package/dist/orchestrator/flow-manager.js +52 -3
  589. package/dist/orchestrator/flow-manager.js.map +1 -1
  590. package/dist/orchestrator/lockstep-version.d.ts +15 -5
  591. package/dist/orchestrator/lockstep-version.d.ts.map +1 -1
  592. package/dist/orchestrator/lockstep-version.js +30 -17
  593. package/dist/orchestrator/lockstep-version.js.map +1 -1
  594. package/dist/orchestrator/project-initializer.d.ts +6 -0
  595. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  596. package/dist/orchestrator/project-initializer.js +23 -0
  597. package/dist/orchestrator/project-initializer.js.map +1 -1
  598. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  599. package/dist/orchestrator/screen-manager.js +52 -4
  600. package/dist/orchestrator/screen-manager.js.map +1 -1
  601. package/dist/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  602. package/dist/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  603. package/dist/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  604. package/dist/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  605. package/dist/orchestrator/templates/ai-src/commands/create-test.md +36 -46
  606. package/dist/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  607. package/dist/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  608. package/dist/orchestrator/templates/ai-src/commands/design.md +2 -0
  609. package/dist/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  610. package/dist/orchestrator/templates/ai-src/commands/locale.md +2 -0
  611. package/dist/orchestrator/templates/ai-src/commands/review.md +2 -0
  612. package/dist/orchestrator/templates/ai-src/commands/run-test.md +6 -8
  613. package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
  614. package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
  615. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  616. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  617. package/dist/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  618. package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  619. package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  620. package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  621. package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +1 -3
  622. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +1 -1
  623. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +0 -18
  624. package/dist/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  625. package/dist/orchestrator/viewpoint-pattern-catalog.d.ts +1 -4
  626. package/dist/orchestrator/viewpoint-pattern-catalog.d.ts.map +1 -1
  627. package/dist/orchestrator/viewpoint-pattern-catalog.js +2 -5
  628. package/dist/orchestrator/viewpoint-pattern-catalog.js.map +1 -1
  629. package/dist/utils/safe-yaml-parse.d.ts +8 -0
  630. package/dist/utils/safe-yaml-parse.d.ts.map +1 -0
  631. package/dist/utils/safe-yaml-parse.js +51 -0
  632. package/dist/utils/safe-yaml-parse.js.map +1 -0
  633. package/package.json +3 -3
  634. package/src/basis/anchor-grammar.ts +83 -0
  635. package/src/basis/anchor-hash.ts +27 -0
  636. package/src/basis/bm51-tables.ts +65 -0
  637. package/src/basis/extract-spec-anchors.ts +95 -0
  638. package/src/basis/migration-sentinel.ts +175 -0
  639. package/src/basis/spec-frontmatter.ts +68 -0
  640. package/src/basis/spec-sections.ts +35 -0
  641. package/src/basis/test-basis-schema.ts +81 -0
  642. package/src/capabilities/registry.ts +16 -0
  643. package/src/cli/commands/audit.ts +3 -26
  644. package/src/cli/commands/capability.ts +1 -1
  645. package/src/cli/commands/contract-derive-command.ts +60 -0
  646. package/src/cli/commands/contract.ts +130 -0
  647. package/src/cli/commands/delivery.ts +52 -3
  648. package/src/cli/commands/generate.ts +3 -1
  649. package/src/cli/commands/journey.ts +39 -14
  650. package/src/cli/commands/next.ts +60 -8
  651. package/src/cli/commands/update.ts +35 -8
  652. package/src/cli/index.ts +20 -32
  653. package/src/contract/approve-contract.ts +94 -0
  654. package/src/contract/assertion-matcher-vocabulary.ts +93 -0
  655. package/src/contract/build-contract-verdict.ts +108 -0
  656. package/src/contract/build-trace-conformance.ts +100 -0
  657. package/src/contract/classify-contract-change.ts +92 -0
  658. package/src/contract/classify-contract-unit.ts +47 -0
  659. package/src/contract/classify-trace-conformance.ts +116 -0
  660. package/src/contract/contract-git.ts +106 -0
  661. package/src/contract/contract-hash.ts +34 -0
  662. package/src/contract/contract-report-file.ts +25 -0
  663. package/src/contract/contract-schema.ts +137 -0
  664. package/src/contract/contract-units.ts +52 -0
  665. package/src/contract/contracts-lock-file.ts +97 -0
  666. package/src/contract/data-normalize.ts +66 -0
  667. package/src/contract/derive-free-feature.ts +110 -0
  668. package/src/contract/field-semantic-redaction.ts +80 -0
  669. package/src/contract/free-feature-blocks.ts +56 -0
  670. package/src/contract/lock-secret-guard.ts +95 -0
  671. package/src/contract/lock-vs-git-head.ts +38 -0
  672. package/src/contract/normalize-contract-step.ts +74 -0
  673. package/src/contract/normalize-feature-steps.ts +93 -0
  674. package/src/contract/oracle-strength.ts +80 -0
  675. package/src/contract/project-contract-blockers.ts +36 -0
  676. package/src/contract/record-contract-approval.ts +70 -0
  677. package/src/contract/render-free-gherkin.ts +113 -0
  678. package/src/contract/render-trace-sentence.ts +98 -0
  679. package/src/contract/render-trace-target.ts +68 -0
  680. package/src/contract/resolve-unit-trace-identities.ts +35 -0
  681. package/src/contract/trace/collect-trace-evidence.ts +109 -0
  682. package/src/contract/trace/identity-cache.ts +61 -0
  683. package/src/contract/trace/select-final-attempt.ts +70 -0
  684. package/src/contract/trace/snapshot-html-renderer.ts +136 -0
  685. package/src/contract/trace/snapshot-resolver.ts +66 -0
  686. package/src/contract/trace/snapshot-store.ts +151 -0
  687. package/src/contract/trace/target-identity.ts +104 -0
  688. package/src/contract/trace/test-step-join.ts +103 -0
  689. package/src/contract/trace/trace-binding.ts +56 -0
  690. package/src/contract/trace/trace-file-reader.ts +123 -0
  691. package/src/contract/trace/trace-fingerprint.ts +68 -0
  692. package/src/contract/trace/trace-identity-resolution.ts +74 -0
  693. package/src/contract/trace/trace-run-record.ts +74 -0
  694. package/src/contract/trace/trace-steps.ts +88 -0
  695. package/src/contract/trace/trace-zip-limits.ts +73 -0
  696. package/src/contract/trace/trace-zip-reader.ts +127 -0
  697. package/src/contract/verify-approval-decisions.ts +60 -0
  698. package/src/decisions/append-decision.ts +35 -0
  699. package/src/decisions/confirmed-decision-commands.ts +128 -0
  700. package/src/decisions/decision-file.ts +93 -0
  701. package/src/decisions/decision-identity.ts +73 -0
  702. package/src/decisions/decision-integrity-check.ts +61 -0
  703. package/src/decisions/decision-schema.ts +76 -0
  704. package/src/decisions/decision-source.ts +14 -0
  705. package/src/decisions/exclusive-file-create.ts +42 -0
  706. package/src/decisions/git-head-trust.ts +32 -0
  707. package/src/decisions/human-confirmation.ts +114 -0
  708. package/src/decisions/legacy-waiver-migration.ts +135 -0
  709. package/src/decisions/read-decisions.ts +61 -0
  710. package/src/decisions/viewpoint-baseline-adapter.ts +74 -0
  711. package/src/decisions/waiver-decisions.ts +56 -0
  712. package/src/exporters/matrix/build.ts +11 -9
  713. package/src/exporters/matrix/export.ts +5 -0
  714. package/src/exporters/matrix/gates.ts +62 -129
  715. package/src/exporters/matrix/render-csv.ts +3 -0
  716. package/src/exporters/matrix/render-gate-overrides-sheet.ts +50 -0
  717. package/src/exporters/matrix/render-xlsx.ts +3 -0
  718. package/src/exporters/matrix/types.ts +4 -1
  719. package/src/generators/test-generator/code-generator.ts +13 -0
  720. package/src/graph/evaluate-graph.ts +62 -0
  721. package/src/graph/locate-finding.ts +93 -0
  722. package/src/graph/node-decl.ts +65 -0
  723. package/src/graph/nodes/core-node-catalog.ts +15 -0
  724. package/src/graph/nodes/test-case-contract-nodes.ts +69 -0
  725. package/src/graph/nodes/test-case-nodes.ts +68 -0
  726. package/src/graph/nodes/unit-audit-nodes.ts +95 -0
  727. package/src/graph/nodes/unit-contract-node.ts +82 -0
  728. package/src/graph/nodes/unit-design-nodes.ts +88 -0
  729. package/src/graph/nodes/unit-execution-nodes.ts +77 -0
  730. package/src/graph/test-case-identity.ts +75 -0
  731. package/src/graph/test-case-results.ts +90 -0
  732. package/src/graph/test-case-satisfied.ts +55 -0
  733. package/src/graph/test-case-state-shape.ts +62 -0
  734. package/src/graph/test-case-states.ts +84 -0
  735. package/src/graph/unit-artifact-probes.ts +77 -0
  736. package/src/graph/unit-state-facts.ts +112 -0
  737. package/src/harness/audit.ts +29 -18
  738. package/src/harness/catalog/gate-rules.yaml +647 -0
  739. package/src/harness/gates/audit-gate-terms.ts +80 -0
  740. package/src/harness/gates/audit-gate-verdict.ts +78 -0
  741. package/src/harness/gates/audit-input-hash.ts +55 -0
  742. package/src/harness/gates/audit-report-verification.ts +173 -0
  743. package/src/harness/gates/decisions-hash.ts +42 -0
  744. package/src/harness/gates/delivery-checklist.ts +120 -0
  745. package/src/harness/gates/delivery-decision-gate.ts +111 -0
  746. package/src/harness/gates/delivery-decision-step.ts +73 -0
  747. package/src/harness/gates/gate-override-report.ts +114 -0
  748. package/src/harness/gates/gate-override-scope.ts +66 -0
  749. package/src/harness/gates/gate-override-validation.ts +75 -0
  750. package/src/harness/gates/gate-rule-catalog.ts +82 -0
  751. package/src/harness/gates/gate-terms.ts +59 -0
  752. package/src/harness/gates/in-run-decision-check.ts +99 -0
  753. package/src/harness/gates/journey-gate-inputs.ts +89 -0
  754. package/src/harness/gates/migrated-waiver-review.ts +68 -0
  755. package/src/harness/gates/resolve-rule.ts +94 -0
  756. package/src/harness/gates/unit-gate-overrides.ts +99 -0
  757. package/src/harness/journey.ts +122 -27
  758. package/src/harness/manifest.ts +67 -43
  759. package/src/harness/next-step.ts +59 -210
  760. package/src/harness/parse.ts +25 -15
  761. package/src/harness/script-check.ts +20 -5
  762. package/src/harness/sensors.ts +55 -49
  763. package/src/harness/viewpoint-baseline.ts +1 -7
  764. package/src/harness/viewpoint-ledger.ts +11 -52
  765. package/src/hooks/bash-write-targets.ts +109 -0
  766. package/src/hooks/claude-hook-entry.ts +85 -0
  767. package/src/hooks/claude-transcript-turn.ts +96 -0
  768. package/src/hooks/guard-delivery-contract-rule.ts +110 -0
  769. package/src/hooks/guard-protected-paths.ts +70 -0
  770. package/src/hooks/guard-rules.ts +98 -0
  771. package/src/hooks/guard-tty-spoof-rule.ts +31 -0
  772. package/src/hooks/hand-back-core.ts +106 -0
  773. package/src/hooks/hook-cli-command.ts +17 -0
  774. package/src/hooks/hook-registry.ts +24 -0
  775. package/src/hooks/hook-result-contract.ts +114 -0
  776. package/src/hooks/hook-toggles.ts +66 -0
  777. package/src/hooks/pre-tool-guard-hook.ts +67 -0
  778. package/src/hooks/session-start-unit-state-hook.ts +54 -0
  779. package/src/hooks/stop-next-step-hook.ts +37 -0
  780. package/src/hooks/sungen-project-root.ts +80 -0
  781. package/src/orchestrator/ai-skills/command-partial-includes.ts +72 -0
  782. package/src/orchestrator/ai-skills/commands.ts +22 -8
  783. package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
  784. package/src/orchestrator/claude-hooks-install.ts +168 -0
  785. package/src/orchestrator/flow-manager.ts +53 -3
  786. package/src/orchestrator/lockstep-version.ts +28 -15
  787. package/src/orchestrator/project-initializer.ts +21 -0
  788. package/src/orchestrator/screen-manager.ts +52 -4
  789. package/src/orchestrator/templates/ai-src/commands/add-api.md +2 -0
  790. package/src/orchestrator/templates/ai-src/commands/add-flow.md +2 -0
  791. package/src/orchestrator/templates/ai-src/commands/add-screen.md +2 -0
  792. package/src/orchestrator/templates/ai-src/commands/create-data-test.md +1 -0
  793. package/src/orchestrator/templates/ai-src/commands/create-test.md +36 -46
  794. package/src/orchestrator/templates/ai-src/commands/dashboard.md +2 -0
  795. package/src/orchestrator/templates/ai-src/commands/delivery.md +2 -0
  796. package/src/orchestrator/templates/ai-src/commands/design.md +2 -0
  797. package/src/orchestrator/templates/ai-src/commands/feedback.md +2 -0
  798. package/src/orchestrator/templates/ai-src/commands/locale.md +2 -0
  799. package/src/orchestrator/templates/ai-src/commands/review.md +2 -0
  800. package/src/orchestrator/templates/ai-src/commands/run-test.md +6 -8
  801. package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
  802. package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
  803. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.claude.md +20 -0
  804. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.codex.md +20 -0
  805. package/src/orchestrator/templates/ai-src/partials/hand-back-rule.copilot.md +20 -0
  806. package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  807. package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  808. package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  809. package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +1 -3
  810. package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +1 -1
  811. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +0 -18
  812. package/src/orchestrator/templates/claude-hooks/sungen-hook.cjs +64 -0
  813. package/src/orchestrator/viewpoint-pattern-catalog.ts +2 -5
  814. package/src/utils/safe-yaml-parse.ts +44 -0
  815. package/dist/cli/commands/viewpoint.d.ts +0 -7
  816. package/dist/cli/commands/viewpoint.d.ts.map +0 -1
  817. package/dist/cli/commands/viewpoint.js +0 -70
  818. package/dist/cli/commands/viewpoint.js.map +0 -1
  819. package/dist/harness/viewpoint-draft-header.d.ts +0 -7
  820. package/dist/harness/viewpoint-draft-header.d.ts.map +0 -1
  821. package/dist/harness/viewpoint-draft-header.js +0 -100
  822. package/dist/harness/viewpoint-draft-header.js.map +0 -1
  823. package/dist/harness/viewpoint-mode.d.ts +0 -9
  824. package/dist/harness/viewpoint-mode.d.ts.map +0 -1
  825. package/dist/harness/viewpoint-mode.js +0 -52
  826. package/dist/harness/viewpoint-mode.js.map +0 -1
  827. package/dist/harness/viewpoint-priority.d.ts +0 -19
  828. package/dist/harness/viewpoint-priority.d.ts.map +0 -1
  829. package/dist/harness/viewpoint-priority.js +0 -36
  830. package/dist/harness/viewpoint-priority.js.map +0 -1
  831. package/dist/harness/viewpoint-review-open.d.ts +0 -6
  832. package/dist/harness/viewpoint-review-open.d.ts.map +0 -1
  833. package/dist/harness/viewpoint-review-open.js +0 -90
  834. package/dist/harness/viewpoint-review-open.js.map +0 -1
  835. package/dist/harness/viewpoint-scan-refs.d.ts +0 -50
  836. package/dist/harness/viewpoint-scan-refs.d.ts.map +0 -1
  837. package/dist/harness/viewpoint-scan-refs.js +0 -200
  838. package/dist/harness/viewpoint-scan-refs.js.map +0 -1
  839. package/dist/harness/viewpoint-scan.d.ts +0 -41
  840. package/dist/harness/viewpoint-scan.d.ts.map +0 -1
  841. package/dist/harness/viewpoint-scan.js +0 -118
  842. package/dist/harness/viewpoint-scan.js.map +0 -1
  843. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +0 -63
  844. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +0 -158
  845. package/dist/orchestrator/viewpoint-scaffold.d.ts +0 -3
  846. package/dist/orchestrator/viewpoint-scaffold.d.ts.map +0 -1
  847. package/dist/orchestrator/viewpoint-scaffold.js +0 -70
  848. package/dist/orchestrator/viewpoint-scaffold.js.map +0 -1
  849. package/src/cli/commands/viewpoint.ts +0 -58
  850. package/src/harness/viewpoint-draft-header.ts +0 -55
  851. package/src/harness/viewpoint-mode.ts +0 -49
  852. package/src/harness/viewpoint-priority.ts +0 -38
  853. package/src/harness/viewpoint-review-open.ts +0 -51
  854. package/src/harness/viewpoint-scan-refs.ts +0 -154
  855. package/src/harness/viewpoint-scan.ts +0 -111
  856. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +0 -63
  857. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +0 -158
  858. package/src/orchestrator/viewpoint-scaffold.ts +0 -36
@@ -12,8 +12,9 @@ import { parse as parseYaml } from 'yaml';
12
12
  import { ScenarioInfo, ViewpointEntry, idPrefix } from './parse';
13
13
  import { readTextFile } from './read-text';
14
14
  import { keywordHit, hasKeyword } from './keyword-match';
15
- import { parsePriority } from './viewpoint-priority';
16
15
  import { countedAssertions } from './counted-assertions';
16
+ import { CATALOG_GATE_RULES, GateRules } from './gates/resolve-rule';
17
+ import { ruleVerdict, sensorSeverityOf } from './gates/gate-terms';
17
18
 
18
19
  // Business-critical category keywords (matched by CONTAINMENT against the VP category, so a
19
20
  // compound category like LIST-DISPLAY / ADD-TO-CART / PRODUCT-DISCOVERY classifies correctly).
@@ -277,6 +278,8 @@ export function assertionDepth(
277
278
  scenarios: ScenarioInfo[],
278
279
  dataThemes: CatalogTheme[] = [],
279
280
  focus = 'functional',
281
+ /** The unit's gate rules; the catalog alone (today's levels) by default. */
282
+ rules: GateRules = CATALOG_GATE_RULES,
280
283
  ): DepthResult {
281
284
  const nonManual = scenarios.filter((s) => !s.manual);
282
285
  const shallow = nonManual.filter((s) => s.shallow);
@@ -300,8 +303,7 @@ export function assertionDepth(
300
303
  const ratio = required.length ? 1 - reqShallow.length / required.length : 1;
301
304
 
302
305
  const threshold = DEPTH_THRESHOLDS[focus] ?? DEPTH_THRESHOLDS.functional;
303
- let verdict: DepthVerdict = 'pass';
304
- if (ratio < threshold) verdict = WARN_ONLY_FOCUS.has(focus) ? 'warn' : 'fail';
306
+ const verdict = ruleVerdict(rules, 'audit.assertion-depth', ratio < threshold, WARN_ONLY_FOCUS.has(focus));
305
307
 
306
308
  return {
307
309
  total: nonManual.length,
@@ -712,7 +714,10 @@ export interface ClaimUnproven {
712
714
  claim: string; // the claim keyword matched in the title
713
715
  need: string; // the assertion shape the steps must contain
714
716
  hint: string; // how to fix
715
- severity: 'fail' | 'warn';
717
+ /** Resolved from the row's catalog rule (fail = blocks when under the ratio; info only via an override). */
718
+ severity: 'fail' | 'warn' | 'info';
719
+ /** The gate rule that decided `severity` (`claim.*`, or `claim.partial-proof`). */
720
+ rule: string;
716
721
  }
717
722
 
718
723
  export interface ClaimProofResult {
@@ -727,16 +732,17 @@ export interface ClaimProofResult {
727
732
  }
728
733
 
729
734
  interface ClaimRule {
735
+ /** The catalog rule (`claim.*`) whose resolved severity a missing proof gets (OES-11.2). */
736
+ rule: string;
730
737
  claim: string;
731
738
  title: RegExp; // claim keyword(s) in the title
732
739
  proof: RegExp; // required assertion shape in the steps
733
740
  need: string;
734
741
  hint: string;
735
- severity: 'fail' | 'warn';
736
742
  // Optional: even when `proof` matches, if `underproof` also matches the proof is
737
- // PARTIAL (the right shape exists but on the wrong/another element) → a warn-level
738
- // finding. e.g. an "each card exposes <action>" claim that has a see-all on price
739
- // but asserts the actions with singular presence.
743
+ // PARTIAL (the right shape exists but on the wrong/another element) → a finding at the
744
+ // `claim.partial-proof` rule's level. e.g. an "each card exposes <action>" claim that has
745
+ // a see-all on price but asserts the actions with singular presence.
740
746
  underproof?: RegExp;
741
747
  underNeed?: string;
742
748
  underHint?: string;
@@ -750,29 +756,30 @@ const CLAIM_RULES: ClaimRule[] = [
750
756
  // catch, so proving it with a substring proves the opposite of what the title says. Found on
751
757
  // a real basic flow whose seven confirmation rows all used `contains` while claiming the six
752
758
  // values "survive unchanged" (#599).
759
+ rule: 'claim.unchanged-preserved',
753
760
  claim: 'unchanged/preserved',
754
761
  title: /\b(unchanged|unmodified|un-?edited|identical|preserved|intact|verbatim|as entered|without (?:any )?(?:change|modification))\b/,
755
762
  proof: /^(?![\s\S]*\bcontains \{\{)/,
756
763
  need: 'exact-value assertions (`see [X] text with {{v}}` / `field with {{v}}`) for the values the title says are unchanged',
757
764
  hint: 'swap `contains {{v}}` for `with {{v}}` on the continuity rows. A substring match cannot distinguish "unchanged" from "changed by adding a prefix" — and if the screen genuinely renders extra text around the value, say so in the title instead of claiming exactness.',
758
- severity: 'warn',
759
765
  },
760
766
  {
761
767
  // An ORDER claim ("sorted by date descending", "ties broken by last-updated") is invisible to
762
768
  // a presence check: `see [List] table` passes whatever order the rows are in. It sits ahead of
763
769
  // the generic rules so "all results sorted …" is judged as an ordering claim, not an all-claim.
764
770
  // A tiebreaker is a Tier-1 guard in the tc-generation skill, yet no rule recognised it (#810).
771
+ rule: 'claim.ordering-sort',
765
772
  claim: 'ordering/sort',
766
773
  title: /\b(ascending|descending|sorted|sorts|sort order|ordered by|order by|in (?:chronological|alphabetical|reverse|numeric) order|(?:newest|oldest|latest|earliest|highest|lowest) first|tie-?break(?:s|er|ers)?|ties?)\b|降順|昇順|並び順|giảm dần|tăng dần|sắp xếp/,
767
774
  proof: /match data|is sorted|sorted (?:ascending|descending)|\brow \d+\b|\.first\.|\.last\.|\[\d+\]\.|order by/,
768
775
  need: 'an order-sensitive assertion (`table match data:` rows in the expected order, `is sorted ascending|descending`, or positional `row N` checks)',
769
776
  hint: 'a presence check passes in any order — assert the rows in their expected sequence (`User see [T] table match data:`), or the column sort state; a tiebreak needs two rows sharing the primary key.',
770
- severity: 'fail',
771
777
  },
772
778
  {
773
779
  // A LIMIT claim ("never exceeds 10", "at most 5 files") is a count claim in disguise.
774
780
  // Ahead of 'no-side-effect', whose negation+effect pattern would otherwise swallow
775
781
  // "never exceeds 10 records" and ask for a duplicate-count proof.
782
+ rule: 'claim.limit-cap',
776
783
  claim: 'limit/cap',
777
784
  // The quantity must be ≥2 ("at most ONE order" is a no-duplicate claim — left to the rules
778
785
  // below) and not a duration ("up to 30 seconds" is a timing claim, not a cap).
@@ -780,51 +787,50 @@ const CLAIM_RULES: ClaimRule[] = [
780
787
  proof: /\bcount\b|tohavecount|table with|row with \{\{|match data|is at most|\bexactly\b|field with \{\{|\berror\b|invalid|is disabled/,
781
788
  need: 'a count/boundary assertion (count is N, `is at most`, the N+1th input rejected or truncated)',
782
789
  hint: 'assert the boundary itself — the count at the cap, or the value/error when one more is attempted — not just that the list or field is present.',
783
- severity: 'fail',
784
790
  },
785
791
  {
792
+ rule: 'claim.all-every-each',
786
793
  claim: 'all/every/each',
787
794
  title: /\b(all|every|each)\b/,
788
795
  proof: /\bsee all\b/,
789
796
  need: 'a `User see all …` assertion (every element, not one)',
790
797
  hint: 'assert across all matched elements — for per-card structure use the all-card DSL (roadmap Q2).',
791
- severity: 'fail',
792
798
  // singular action presence under an "each/every" claim = partial proof
793
799
  underproof: /\bsee \[[^\]]+\] (link|button)\b/,
794
800
  underNeed: 'an all-card assertion for the claimed action (not single presence)',
795
801
  underHint: 'the actions are asserted once, not per-card — wrap them with the all-card DSL `User see all [Card] contain [Action]` (roadmap Q2).',
796
802
  },
797
803
  {
804
+ rule: 'claim.only-belong-to',
798
805
  claim: 'only/belong-to',
799
806
  title: /\b(only|belongs? to|exclusively)\b/,
800
807
  proof: /\bsee all\b[^.]*\bcontain/,
801
808
  need: 'all-match (`User see all … contains …`)',
802
809
  hint: 'all displayed items must match the facet; "no items OUTSIDE" is UI-limited → verify via hybrid API/DB or mark @manual with reason.',
803
- severity: 'warn',
804
810
  },
805
811
  {
812
+ rule: 'claim.single-exactly-one',
806
813
  claim: 'single/exactly-one',
807
814
  title: /\b(a single|single|exactly one|just one|only one)\b/,
808
815
  proof: /\bcount\b|\btable with\b|\bexactly\b|tohavecount/,
809
816
  need: 'a count assertion (e.g. count is 1)',
810
817
  hint: 'assert the quantity, not just presence.',
811
- severity: 'fail',
812
818
  },
813
819
  {
820
+ rule: 'claim.correct-matching-identity',
814
821
  claim: 'correct/matching/identity',
815
822
  title: /\b(correct|matching|the same|same (product|name|price|value|item)|consistency|identity|lands? in|is the (product|item))\b/,
816
823
  proof: /\bremember\b/,
817
824
  need: 'capture + compare (`User remember … as {{v}}` then assert {{v}})',
818
825
  hint: 'remember the value on the source, then assert it on the target (cross-screen → defer to a flow as @manual).',
819
- severity: 'fail',
820
826
  },
821
827
  {
828
+ rule: 'claim.changes-toggles',
822
829
  claim: 'changes/toggles',
823
830
  title: /\b(changes?|toggles?|updates?|reflects?|reopens?)\b/,
824
831
  proof: /\bremember\b|\bis hidden\b/,
825
832
  need: 'a state delta (before ≠ after, or a visible→hidden contrast)',
826
833
  hint: 'capture the before-state and assert the after-state differs, or assert the visible/hidden transition.',
827
- severity: 'warn',
828
834
  },
829
835
  {
830
836
  // GENERAL — mutation-absence. A title asserts that a STATE-CHANGING action does NOT
@@ -835,6 +841,7 @@ const CLAIM_RULES: ClaimRule[] = [
835
841
  // count/contrast (record count unchanged) or defer to @manual. This is the general
836
842
  // category behind "browser back does not re-submit", "does not re-charge the card",
837
843
  // "double-click does not create two orders" — not a per-feature keyword.
844
+ rule: 'claim.no-side-effect',
838
845
  claim: 'no-side-effect/no-duplicate',
839
846
  // The negation must attach to an EFFECT — something created, sent, stored, charged, fired
840
847
  // twice — not to the INPUT being refused. "duplicate address is rejected" is an observable
@@ -852,33 +859,33 @@ const CLAIM_RULES: ClaimRule[] = [
852
859
  proof: /\bcount\b|ok_count|status_counts|row with \{\{|table with|tohavecount|is hidden|are hidden|is disabled|are disabled|not complete|no longer/,
853
860
  need: 'a record/request-count proof (count stays at one, e.g. `User see [Table] row with {{count}}`, an API `{{name.ok_count}}` invariant, or a `@query` DB count) or @manual with a request-count oracle',
854
861
  hint: 'a "does-not-happen / does-not-repeat" claim about a state-changing action is NOT proven by a terminal `see [...] page` — that page is identical whether or not the action (re-)fired. Prove the side-effect count is unchanged, or mark @manual with a setup→action→assert-no-duplicate oracle.',
855
- severity: 'fail',
856
862
  },
857
863
  {
864
+ rule: 'claim.hidden-rejected',
858
865
  claim: 'hidden/rejected/not-complete',
859
866
  title: /\b(hidden|closed|dismiss(es|ed)?|not complete|rejected|inert)\b/,
860
867
  proof: /\bis hidden\b|\bare hidden\b|message is hidden|not complete|\bhidden\b/,
861
868
  need: 'a negative / hidden assertion (`… is hidden`)',
862
869
  hint: 'assert the absence/hidden state that the title claims, not just an unrelated visible element.',
863
- severity: 'fail',
864
870
  },
865
871
  {
872
+ rule: 'claim.cleared-emptied',
866
873
  claim: 'cleared/emptied',
867
874
  title: /\b(cleared|clears|emptied|empties|reset to empty|wiped)\b/,
868
875
  proof: /\bis empty\b|with \{\{empty|with ['"]?['"]?\s*$|\bempty\b/,
869
876
  need: 'an empty/cleared assertion after the action (e.g. `field with {{empty_value}}` / `is empty`)',
870
877
  hint: 'prove the value is actually gone — return to the screen and assert the field is empty, not just that the action ran.',
871
- severity: 'fail',
872
878
  },
873
879
  {
880
+ rule: 'claim.restored-preserved',
874
881
  claim: 'restored/preserved',
875
882
  title: /\b(restored|preserved|persists?|retained|remembered|kept)\b/,
876
883
  proof: /\bremember\b|with \{\{|field with/,
877
884
  need: 'the value re-asserted after the transition (capture or `field with {{v}}` after returning)',
878
885
  hint: 'prove the value survives — assert the field still holds the typed value after the reload/return, not just that it was typed.',
879
- severity: 'warn',
880
886
  },
881
887
  {
888
+ rule: 'claim.independent-separate',
882
889
  claim: 'independent/separate',
883
890
  title: /\b(independent|separate|isolat(ed|es)|per[- ]tab|two tabs|each tab)\b/,
884
891
  // A storage `does not exist` check on the OTHER tenant's key proves isolation
@@ -887,23 +894,22 @@ const CLAIM_RULES: ClaimRule[] = [
887
894
  proof: /\bcontext\b|tab a|tab b|second (tab|context)|does not exist/,
888
895
  need: 'a multi-context proof (tab A vs tab B)',
889
896
  hint: 'independence across tabs/contexts is rarely DSL-expressible — mark @manual with a clear setup/action/oracle.',
890
- severity: 'warn',
891
897
  },
892
898
  {
899
+ rule: 'claim.sanitized-inert',
893
900
  claim: 'sanitized/inert',
894
901
  title: /\b(sanitized|sanitised|escaped|inert|not executed|not rendered|stripped)\b/,
895
902
  proof: /field with \{\{|payload|inert|toContainText|is hidden/,
896
903
  need: 'the payload echoed as inert text (`field with {{payload}}`) + no execution',
897
904
  hint: 'prove the payload round-trips as literal text and triggers nothing — assert the field value and the absence of any effect.',
898
- severity: 'warn',
899
905
  },
900
906
  {
907
+ rule: 'claim.announces-aria',
901
908
  claim: 'announces/aria',
902
909
  title: /\b(announce[sd]?|aria|screen[- ]reader|programmatically associated)\b/,
903
910
  proof: /aria|role|@manual|describedby|is focused|tab order/,
904
911
  need: 'an aria/role assertion (or @manual with a screen-reader oracle)',
905
912
  hint: 'ARIA announcement is usually not DSL-expressible — assert aria attributes if possible, else @manual with an NVDA/VoiceOver oracle.',
906
- severity: 'warn',
907
913
  },
908
914
  {
909
915
  // GENERAL CATCH-ALL (last) — any negative/absence title not handled by a specific
@@ -913,6 +919,7 @@ const CLAIM_RULES: ClaimRule[] = [
913
919
  // only a positive presence. WARN, because a positive proxy is sometimes a valid
914
920
  // negative proof (e.g. "stayed on the login page"); the semantic reviewer is the
915
921
  // authoritative recall layer for the residue this can't judge structurally.
922
+ rule: 'claim.negative-absence',
916
923
  claim: 'negative-claim/absence',
917
924
  title: /(\bno\b|\bnot\b|n['’]t\b|\bnever\b|\bwithout\b|\bcannot\b|prevent|block|avoid|reject|disabl|\bdeny\b|denies|\bkhông\b|\bchưa\b)/i,
918
925
  // `does not exist` covers the storage-absence shape — the broad "any storage
@@ -922,7 +929,6 @@ const CLAIM_RULES: ClaimRule[] = [
922
929
  proof: /is hidden|are hidden|is empty|no longer|not complete|disabl|invalid|rejected|\berror\b|\bcount\b|row with \{\{|table with|\bremember\b|\bexactly\b|tohavecount|does not exist|tab order/i,
923
930
  need: 'a proof of the ABSENCE — a contrast/empty/hidden/error/count assertion, or @manual with an oracle',
924
931
  hint: 'a negative claim ("no / not / không …") is not proven by a positive `see [X]` that looks the same whether or not the claim holds. Assert the contrast (state hidden/empty, error shown, count unchanged), or mark @manual.',
925
- severity: 'warn',
926
932
  },
927
933
  ];
928
934
 
@@ -1015,7 +1021,12 @@ export function taxonomyLint(scenarios: ScenarioInfo[]): TaxonomyResult {
1015
1021
  return { total: scenarios.length, checked, mislabeled };
1016
1022
  }
1017
1023
 
1018
- export function claimProof(scenarios: ScenarioInfo[], focus = 'functional'): ClaimProofResult {
1024
+ /**
1025
+ * `rules` resolves each row's catalog rule for the unit being audited (the catalog alone by default —
1026
+ * today's levels). A rule switched `off` by a gate-override is not measured: its unproven claim is
1027
+ * neither counted nor reported (a partial-proof switched off leaves the claim proven).
1028
+ */
1029
+ export function claimProof(scenarios: ScenarioInfo[], focus = 'functional', rules: GateRules = CATALOG_GATE_RULES): ClaimProofResult {
1019
1030
  const measured = scenarios.filter((s) => !s.manual);
1020
1031
  const unproven: ClaimUnproven[] = [];
1021
1032
  let withClaims = 0;
@@ -1024,19 +1035,17 @@ export function claimProof(scenarios: ScenarioInfo[], focus = 'functional'): Cla
1024
1035
  const title = s.name.toLowerCase();
1025
1036
  const rule = CLAIM_RULES.find((r) => r.title.test(title));
1026
1037
  if (!rule) continue; // no claim → not measured
1038
+ const miss = !rule.proof.test(s.stepsText)
1039
+ // proof shape entirely absent → the claim's own rule decides the level
1040
+ ? { rule: rule.rule, need: rule.need, hint: rule.hint }
1041
+ : rule.underproof && rule.underproof.test(s.stepsText)
1042
+ // right shape exists but only as a partial proof
1043
+ ? { rule: 'claim.partial-proof', need: rule.underNeed ?? rule.need, hint: rule.underHint ?? rule.hint }
1044
+ : null;
1045
+ const severity = miss ? sensorSeverityOf(rules.resolve(miss.rule)) : undefined;
1046
+ if (miss && severity === null && miss.rule === rule.rule) continue; // the claim's rule is off for this unit
1027
1047
  withClaims++;
1028
- if (!rule.proof.test(s.stepsText)) {
1029
- // proof shape entirely absent → fail at the rule's severity
1030
- unproven.push({ name: s.name, claim: rule.claim, need: rule.need, hint: rule.hint, severity: rule.severity });
1031
- } else if (rule.underproof && rule.underproof.test(s.stepsText)) {
1032
- // right shape exists but only as a partial proof → warn
1033
- unproven.push({
1034
- name: s.name, claim: rule.claim,
1035
- need: rule.underNeed ?? rule.need,
1036
- hint: rule.underHint ?? rule.hint,
1037
- severity: 'warn',
1038
- });
1039
- }
1048
+ if (miss && severity) unproven.push({ name: s.name, claim: rule.claim, need: miss.need, hint: miss.hint, severity, rule: miss.rule });
1040
1049
  }
1041
1050
 
1042
1051
  const proven = withClaims - unproven.length;
@@ -1044,10 +1053,7 @@ export function claimProof(scenarios: ScenarioInfo[], focus = 'functional'): Cla
1044
1053
  const threshold = DEPTH_THRESHOLDS[focus] ?? DEPTH_THRESHOLDS.functional;
1045
1054
 
1046
1055
  const hasFail = unproven.some((u) => u.severity === 'fail');
1047
- let verdict: DepthVerdict = 'pass';
1048
- if (ratio < threshold) {
1049
- verdict = WARN_ONLY_FOCUS.has(focus) || !hasFail ? 'warn' : 'fail';
1050
- }
1056
+ const verdict = ruleVerdict(rules, 'audit.claim-proof', ratio < threshold, WARN_ONLY_FOCUS.has(focus) || !hasFail);
1051
1057
 
1052
1058
  return { total: measured.length, withClaims, proven, unproven, ratio, focus, threshold, verdict };
1053
1059
  }
@@ -1103,6 +1109,8 @@ export function priorityMismatches(
1103
1109
  const declared = new Map<string, string>();
1104
1110
  for (const v of viewpoints) if (v.priorityWord) declared.set(v.id.toUpperCase(), v.priorityWord);
1105
1111
  if (declared.size === 0) return [];
1112
+ // `medium` and `normal` are the same rank under different names in real documents.
1113
+ const rank = (p: string): string => (p === 'medium' ? 'normal' : p);
1106
1114
  const out: Array<{ id: string; declared: string; tagged: string; scenario: string }> = [];
1107
1115
  for (const s of scenarios) {
1108
1116
  const id = (s.vpId ?? '').toUpperCase();
@@ -1110,14 +1118,12 @@ export function priorityMismatches(
1110
1118
  // The most specific declared row that prefixes this id.
1111
1119
  const key = [...declared.keys()].filter((k) => id.startsWith(k)).sort((a, b) => b.length - a.length)[0];
1112
1120
  if (!key) continue;
1113
- // Both sides fold through the one priority reading (`critical` ≡ high, `medium`/`med` ≡ normal).
1114
- const want = parsePriority(declared.get(key)!);
1115
- // `deferred` and `skip` (declared not applicable, #853) have no tag equivalent — nothing to contradict.
1116
- if (want === 'deferred' || want === 'skip') continue;
1117
- // An untagged scenario states no priority, so it cannot contradict one (#861).
1118
- const tagged = s.priorityTag ?? (s.priority === 'unknown' ? undefined : String(s.priority));
1119
- if (!tagged) continue;
1120
- if (want !== parsePriority(tagged)) out.push({ id: key, declared: declared.get(key)!, tagged, scenario: s.name });
1121
+ const want = rank(declared.get(key)!);
1122
+ const got = rank(String(s.priority).toLowerCase());
1123
+ // `critical`, `deferred` and `skip` (declared not applicable, #853) have no tag equivalent —
1124
+ // nothing to contradict.
1125
+ if (want === 'critical' || want === 'deferred' || want === 'skip') continue;
1126
+ if (want !== got) out.push({ id: key, declared: declared.get(key)!, tagged: got, scenario: s.name });
1121
1127
  }
1122
1128
  return out;
1123
1129
  }
@@ -2,7 +2,6 @@ import * as crypto from 'crypto';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
  import { parseViewpointOverview } from './parse';
5
- import { isAiDraft, removeDraftLine } from './viewpoint-draft-header';
6
5
 
7
6
  /**
8
7
  * Keep the yardstick from moving with the thing it measures.
@@ -90,12 +89,7 @@ function fingerprint(ids: string[]): string {
90
89
  */
91
90
  function fileFingerprint(viewpointPath: string): string {
92
91
  try {
93
- const bytes = fs.readFileSync(viewpointPath);
94
- // `drafted-by: ai` is provenance that accept REMOVES, so it is not part of what was reviewed:
95
- // counted, accepting a draft would read as the declaration changing. Every other byte stays raw.
96
- const text = bytes.toString('utf-8');
97
- const hashed = isAiDraft(text) ? Buffer.from(removeDraftLine(text), 'utf-8') : bytes;
98
- return crypto.createHash('sha1').update(hashed).digest('hex').slice(0, 12);
92
+ return crypto.createHash('sha1').update(fs.readFileSync(viewpointPath)).digest('hex').slice(0, 12);
99
93
  } catch { return ''; }
100
94
  }
101
95
 
@@ -58,13 +58,6 @@ function claimedCount(text: string): number | null {
58
58
  }
59
59
 
60
60
  const ID_RE = /\b([A-Z]{1,5}\d{0,2}(?:[.\-][A-Za-z0-9]+)*-?\d{0,3})\b/; // VP0.Title, VP7-002, MS-HP-001, TV-01
61
- /**
62
- * A bullet declares only the id it STARTS with (after a `[ ]` checkbox); one further along is a
63
- * citation (`… (SG-01, …)`). `- **High** VP-…` or `- Email shown — VP-…` therefore carry no id.
64
- */
65
- const BULLET_ID = new RegExp(`^[\\s(#]*(?:\\[[ xX]\\]\\s*)?\\[?${ID_RE.source}`);
66
- /** A table cell that is an id and nothing else. */
67
- const WHOLE_ID = new RegExp(`^${ID_RE.source}$`);
68
61
 
69
62
  /**
70
63
  * Lines that DECLARE something about the checklist rather than being a checklist item —
@@ -109,31 +102,17 @@ const PROCEDURE_LEAD = /^(?:how to (?:run|test|reproduce|verify)|(?:manual )?pro
109
102
  const LIST_ITEM = /^(?:[-*+]|\d+[.)])\s+/;
110
103
  const GENERIC = new Set(['display', 'shown', 'value', 'field', 'input', 'page', 'screen', 'button', 'link', 'text', 'check', 'verify', 'should', 'with', 'when', 'then', 'user', 'this', 'that', 'each', 'item', 'items']);
111
104
 
112
- /**
113
- * `decision`: a `- D4: …` line under Design Decisions. It names the spec's intent so rows can cite
114
- * it; its testable part is carried by a VP row (review check 8). No scenario ever carries `D4`, so
115
- * as a ledger item it stayed MISSING for good — "not tested" decisions included (phase-7 D5). The
116
- * ledger skips it; the continuity, source and integrity checks still read it. Only that section
117
- * declares: a QA claim labelled `D8` elsewhere stays a claim, and an id-less decision bullet stays
118
- * the prose claim it always was.
119
- */
120
- export interface ViewpointItem { id?: string; text: string; decision?: true }
121
- const DECISION_ID = /^D\d+$/;
122
- const DECISIONS_SECTION = /^(?:design decisions?|quyết định(?: thiết kế)?)\b/i;
123
-
124
105
  /** Extract atomic checklist items from a viewpoint file (format-tolerant). */
125
- export function parseViewpointItems(viewpointPath: string): ViewpointItem[] {
106
+ export function parseViewpointItems(viewpointPath: string): { id?: string; text: string }[] {
126
107
  if (!fs.existsSync(viewpointPath)) return [];
127
108
  const lines = readViewpointText(viewpointPath).split('\n');
128
- const items: ViewpointItem[] = [];
109
+ const items: { id?: string; text: string }[] = [];
129
110
  let inFence = false;
130
111
  let inDeclarationSection = false;
131
112
  // The level an excluded section was opened at. A SUBSECTION inherits it: `### Optional`
132
113
  // nested under `## 8. Traceability Index` used to re-open the section and hand its rows
133
114
  // back as claims, because exclusion was recomputed from scratch at every heading.
134
115
  let excludedAt: number | null = null;
135
- // The level the Design Decisions section was opened at, while inside it (see `decision`).
136
- let decisionsAt: number | null = null;
137
116
  // Inside the list a PROCEDURE_LEAD introduced: the indent of the lead (-1 for a paragraph lead),
138
117
  // whose list items — and their wrapped continuation lines — are steps. A heading, a table, a
139
118
  // new lead, or (after a paragraph lead) a blank line once a step was read ends it: when in
@@ -168,28 +147,21 @@ export function parseViewpointItems(viewpointPath: string): ViewpointItem[] {
168
147
  if (heading) {
169
148
  const level = heading[1].length;
170
149
  if (excludedAt !== null && level > excludedAt) continue; // still inside the excluded section
171
- if (decisionsAt !== null && level > decisionsAt) continue; // a subsection of Design Decisions
172
150
  // A heading both ends the previous section and decides whether this one declares or claims.
173
151
  // Strip an ordinal prefix ("## 5. Known Issues", "## 8. Viewpoint Grouping") before
174
152
  // testing. The regex anchors at the start, so a NUMBERED heading matched nothing and
175
153
  // every excluded section leaked its rows back in as claims — a real viewpoint numbers
176
154
  // its sections, so in practice the exclusion list was doing nothing at all (#657).
177
- // `Phần 4:` / `Part 4.` / `Mục 4.` number a section the same way.
178
- const headText = heading[2].replace(/[*`]/g, '').replace(/^\s*(?:(?:[Pp]hần|[Pp]art|[Mm]ục|[Ss]ection)\s+)?(?:\d+|[A-Z])\s*[.):]\s*/, '').trim();
155
+ const headText = heading[2].replace(/[*`]/g, '').replace(/^\s*(?:\d+|[A-Z])\s*[.):]\s*/, '').trim();
179
156
  inDeclarationSection = DECLARATION_SECTION.test(headText);
180
157
  excludedAt = inDeclarationSection ? level : null;
181
- decisionsAt = DECISIONS_SECTION.test(headText) ? level : null;
182
158
  continue;
183
159
  }
184
160
  if (inDeclarationSection) continue;
185
161
  let text = '';
186
162
  let lead = '';
187
- let declared: RegExpMatchArray | null = null;
188
163
  const bullet = line.match(/^(?:[-*+]|\d+[.)])\s+(.*)$/);
189
- if (bullet) {
190
- text = bullet[1]; lead = bullet[1];
191
- declared = lead.replace(/[*`]/g, '').match(BULLET_ID);
192
- }
164
+ if (bullet) { text = bullet[1]; lead = bullet[1]; }
193
165
  else if (line.startsWith('|')) { // table data row
194
166
  if (/^\|[\s|:-]+\|?$/.test(line)) continue; // separator
195
167
  // The row right above a `|---|` separator IS the header, whatever its words (`Skill #`,
@@ -200,14 +172,7 @@ export function parseViewpointItems(viewpointPath: string): ViewpointItem[] {
200
172
  const cells = line.split('|').map((c) => c.trim()).filter(Boolean);
201
173
  if (/^(vp|id|viewpoint|priority|reason|no\.?|category|item|trigger|#|pattern|applicable|notes|field|constraint|code|description|status|step|flow|ref|level|question|screen|actor|branches from|own steps|component|thành phần|trường|bước)$/i.test(cells[0] || '')) continue; // header
202
174
  text = cells.join(' — ');
203
- // The lead is the row's FIRST cell, empty or not: a continuation row (`| | | case | … see SG-02 |`)
204
- // declares nothing. A later cell holding an id and nothing else still declares (`| x | VP-UI-001 |`),
205
- // and so does a row number followed by a cell that starts with one (`| 1 | VP-UI-001 — … |`).
206
- const raw = line.replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.replace(/[*`]/g, '').trim());
207
- lead = raw[0] ?? '';
208
- declared = lead.match(ID_RE)
209
- ?? (/^\d+\.?$/.test(lead) ? (raw[1] ?? '').match(BULLET_ID) : null)
210
- ?? raw.slice(1).map((c) => c.match(WHOLE_ID)).find(Boolean) ?? null;
175
+ lead = cells[0] ?? '';
211
176
  } else continue;
212
177
  text = text.replace(/[*`]/g, '').trim();
213
178
  if (!text) continue;
@@ -222,16 +187,15 @@ export function parseViewpointItems(viewpointPath: string): ViewpointItem[] {
222
187
  // under a grouping section. It declares nothing; it points at things declared elsewhere.
223
188
  const leadIds = new Set((leadClean.match(new RegExp(ID_RE.source, 'g')) || []).filter((x) => /\d/.test(x)));
224
189
  if (leadIds.size > 1) continue;
225
- const id = declared && /\d/.test(declared[1]) ? declared[1] : undefined; // require a digit so prose words aren't IDs
190
+ const idM = leadClean.match(ID_RE) ?? text.match(ID_RE);
191
+ const id = idM && /\d/.test(idM[1]) ? idM[1] : undefined; // require a digit so prose words aren't IDs
226
192
  // A row keyed by a bare GROUP prefix ("VP-LOGIC — transitions — High — why the flow exists")
227
193
  // declares a family and its priority, not a checklist item: nothing can "cover" it, and
228
194
  // demanding coverage sends the author looking for a test that was never meant to exist.
229
195
  if (/^VP-[A-Z]+\s*(?:—|-|\||$)/.test(leadClean) && !/\d/.test(leadClean)) continue;
230
196
  const words = (text.toLowerCase().match(/[a-z][a-z-]{3,}/g) || []).filter((w) => !GENERIC.has(w));
231
197
  if (!id && words.length < 2) continue; // not substantive enough to track
232
- items.push(decisionsAt !== null && id && DECISION_ID.test(id)
233
- ? { id, text: text.slice(0, 100), decision: true }
234
- : { id, text: text.slice(0, 100) });
198
+ items.push({ id, text: text.slice(0, 100) });
235
199
  }
236
200
  return items;
237
201
  }
@@ -242,7 +206,6 @@ export function viewpointLedger(viewpointPath: string, scenarios: ScenarioInfo[]
242
206
  // (#622) — silently collapsing those would remove its input.
243
207
  const seen = new Set<string>();
244
208
  const items = parseViewpointItems(viewpointPath)
245
- .filter((i) => !i.decision)
246
209
  .filter((i) => { const k = `${i.id ?? ''}|${i.text}`; if (seen.has(k)) return false; seen.add(k); return true; });
247
210
  if (!fs.existsSync(viewpointPath) || items.length === 0) {
248
211
  return { hasViewpoint: fs.existsSync(viewpointPath), total: 0, covered: 0, ratio: 1, missing: [], manualOnly: [], partial: [] };
@@ -434,15 +397,11 @@ export function viewpointIntegrity(
434
397
 
435
398
  // The Priority-Viewpoints table: `| <id> | <priority> | <reason> |`.
436
399
  const vpText = readViewpointText(viewpointPath);
437
- const words = [...vpText
400
+ const rows = [...vpText
438
401
  .matchAll(new RegExp(`^\\|\\s*((?:VP|FL)[A-Z0-9._-]*)\\s*\\|\\s*(${PRIORITY_WORDS})\\s*\\|`, 'gim'))]
439
402
  .map((m) => m[2].toLowerCase());
440
- // Fold only synonyms (Medium / Med / Normal are one level). `critical` stays its own word: a
441
- // Critical / High / High table is a ranking, and folding it into `high` made it read as uniform.
442
- const level = (w: string): string => (/^(?:medium|med|normal)$/.test(w) ? 'normal' : w);
443
- const uniformPriority = words.length >= 3 && new Set(words.map(level)).size === 1
444
- ? { levels: [words[0]], rows: words.length } // the word QA wrote, not the folded key
445
- : null;
403
+ const levels = [...new Set(rows)];
404
+ const uniformPriority = rows.length >= 3 && levels.length === 1 ? { levels, rows: rows.length } : null;
446
405
 
447
406
  return { duplicateIds, danglingRefs, uniformPriority };
448
407
  }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The files a Bash command line WRITES through the three forms the pre-tool guard covers (P0.1):
3
+ * output redirection (`>`, `>>`, `>|`, `&>`, `2>`…), `tee`, and in-place `sed -i`.
4
+ *
5
+ * A floor, not a shell interpreter: quotes, escapes, `;`/`&&`/`||`/`|` and a leading `cd` are
6
+ * understood; anything else that can write a file (`cp`, a script, `python -c`) is out of scope by
7
+ * design. The command string is untrusted — it is only tokenized here, never executed.
8
+ */
9
+ import * as path from 'path';
10
+
11
+ type Tok = { kind: 'word'; value: string } | { kind: 'sep' } | { kind: 'redir'; writes: boolean };
12
+
13
+ /** Commands that run another command: the real one is the next word. */
14
+ const WRAPPERS = new Set(['sudo', 'env', 'command', 'nohup', 'time', 'exec', 'nice']);
15
+ const ASSIGNMENT = /^[A-Za-z_]\w*=/;
16
+
17
+ function tokenize(cmd: string): Tok[] {
18
+ const toks: Tok[] = [];
19
+ let word = '';
20
+ let inWord = false;
21
+ const flush = (): void => { if (inWord) toks.push({ kind: 'word', value: word }); word = ''; inWord = false; };
22
+ for (let i = 0; i < cmd.length;) {
23
+ const ch = cmd[i];
24
+ if (ch === "'") {
25
+ const end = cmd.indexOf("'", i + 1);
26
+ word += cmd.slice(i + 1, end < 0 ? cmd.length : end); inWord = true; i = end < 0 ? cmd.length : end + 1;
27
+ } else if (ch === '"') {
28
+ let j = i + 1;
29
+ for (; j < cmd.length && cmd[j] !== '"'; j++) { if (cmd[j] === '\\' && j + 1 < cmd.length) j++; word += cmd[j]; }
30
+ inWord = true; i = j + 1;
31
+ } else if (ch === '\\' && i + 1 < cmd.length) {
32
+ word += cmd[i + 1]; inWord = true; i += 2;
33
+ } else if (ch === ' ' || ch === '\t') {
34
+ flush(); i++;
35
+ } else if ('\n;|()'.includes(ch) || (ch === '&' && cmd[i + 1] !== '>')) {
36
+ flush(); toks.push({ kind: 'sep' }); i += (ch === '|' || ch === '&') && cmd[i + 1] === ch ? 2 : 1;
37
+ } else if (ch === '>' || (ch === '&' && cmd[i + 1] === '>')) {
38
+ if (ch === '>' && inWord && /^\d+$/.test(word)) { word = ''; inWord = false; } else flush(); // `2>`: an fd, not a word
39
+ i += ch === '&' ? 2 : 1;
40
+ if (cmd[i] === '>' || cmd[i] === '|') i++;
41
+ if (cmd[i] === '&') { i++; toks.push({ kind: 'redir', writes: false }); } // `>&2`: an fd dup
42
+ else toks.push({ kind: 'redir', writes: true });
43
+ } else if (ch === '<') {
44
+ flush(); i++;
45
+ while (cmd[i] === '<') i++;
46
+ toks.push({ kind: 'redir', writes: false });
47
+ } else {
48
+ word += ch; inWord = true; i++;
49
+ }
50
+ }
51
+ flush();
52
+ return toks;
53
+ }
54
+
55
+ const isInPlaceFlag = (a: string): boolean => a === '--in-place' || a.startsWith('--in-place=') || /^-[a-zA-Z]*i/.test(a);
56
+
57
+ /** One simple command of a Bash line (`a | b && c` is three). */
58
+ export interface BashSegment {
59
+ /** The command name (basename, after env assignments and wrappers like `sudo`), or ''. */
60
+ cmd: string;
61
+ /** Its arguments, and every redirection target, as written. */
62
+ words: string[];
63
+ /** The directory it runs in — a preceding `cd` in the same line is followed. */
64
+ dir: string;
65
+ /** Absolute paths it writes via redirection, `tee` or `sed -i`. */
66
+ writes: string[];
67
+ }
68
+
69
+ export function bashSegments(command: string, cwd: string): BashSegment[] {
70
+ const out: BashSegment[] = [];
71
+ const toks = tokenize(command);
72
+ let dir = cwd;
73
+ let seg: string[] = [];
74
+ let redirected: string[] = [];
75
+ let writes: string[] = [];
76
+ const endSegment = (): void => {
77
+ let k = 0;
78
+ while (k < seg.length && (ASSIGNMENT.test(seg[k]) || WRAPPERS.has(seg[k]))) k++;
79
+ const cmd = path.basename(seg[k] ?? '');
80
+ const args = seg.slice(k + 1).filter((a) => a !== '');
81
+ if (cmd === 'tee') writes.push(...args.filter((a) => !a.startsWith('-')).map((a) => path.resolve(dir, a)));
82
+ else if ((cmd === 'sed' || cmd === 'gsed') && args.some(isInPlaceFlag)) {
83
+ writes.push(...args.filter((a) => !a.startsWith('-')).map((a) => path.resolve(dir, a)));
84
+ }
85
+ if (cmd || redirected.length) out.push({ cmd, words: [...args, ...redirected], dir, writes });
86
+ if (cmd === 'cd' && args.length === 1 && !/^[-~]/.test(args[0])) dir = path.resolve(dir, args[0]);
87
+ seg = []; redirected = []; writes = [];
88
+ };
89
+ for (let t = 0; t < toks.length; t++) {
90
+ const tok = toks[t];
91
+ if (tok.kind === 'sep') { endSegment(); continue; }
92
+ if (tok.kind === 'redir') {
93
+ const target = toks[t + 1];
94
+ if (target?.kind !== 'word') continue;
95
+ t++;
96
+ redirected.push(target.value);
97
+ if (tok.writes && target.value && target.value !== '/dev/null' && target.value !== '-') writes.push(path.resolve(dir, target.value));
98
+ continue;
99
+ }
100
+ seg.push(tok.value);
101
+ }
102
+ endSegment();
103
+ return out;
104
+ }
105
+
106
+ /** Absolute paths the command writes via redirection, `tee` or `sed -i`, resolved against `cwd`. */
107
+ export function bashWriteTargets(command: string, cwd: string): string[] {
108
+ return bashSegments(command, cwd).flatMap((s) => s.writes);
109
+ }