@ontrails/warden 1.0.0-beta.13 → 1.0.0-beta.15

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 (474) hide show
  1. package/.turbo/turbo-lint.log +1 -1
  2. package/CHANGELOG.md +30 -0
  3. package/README.md +31 -20
  4. package/dist/cli.d.ts +19 -2
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +261 -64
  7. package/dist/cli.js.map +1 -1
  8. package/dist/draft.d.ts +5 -0
  9. package/dist/draft.d.ts.map +1 -0
  10. package/dist/draft.js +16 -0
  11. package/dist/draft.js.map +1 -0
  12. package/dist/drift.d.ts +10 -7
  13. package/dist/drift.d.ts.map +1 -1
  14. package/dist/drift.js +50 -16
  15. package/dist/drift.js.map +1 -1
  16. package/dist/formatters.d.ts +2 -1
  17. package/dist/formatters.d.ts.map +1 -1
  18. package/dist/formatters.js +15 -4
  19. package/dist/formatters.js.map +1 -1
  20. package/dist/index.d.ts +9 -17
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +10 -17
  23. package/dist/index.js.map +1 -1
  24. package/dist/rules/ast.d.ts +412 -7
  25. package/dist/rules/ast.d.ts.map +1 -1
  26. package/dist/rules/ast.js +1847 -102
  27. package/dist/rules/ast.js.map +1 -1
  28. package/dist/rules/circular-refs.d.ts +6 -0
  29. package/dist/rules/circular-refs.d.ts.map +1 -0
  30. package/dist/rules/circular-refs.js +83 -0
  31. package/dist/rules/circular-refs.js.map +1 -0
  32. package/dist/rules/context-no-surface-types.d.ts.map +1 -1
  33. package/dist/rules/context-no-surface-types.js +59 -3
  34. package/dist/rules/context-no-surface-types.js.map +1 -1
  35. package/dist/rules/contour-exists.d.ts +7 -0
  36. package/dist/rules/contour-exists.d.ts.map +1 -0
  37. package/dist/rules/contour-exists.js +113 -0
  38. package/dist/rules/contour-exists.js.map +1 -0
  39. package/dist/rules/contour-ids.d.ts +10 -0
  40. package/dist/rules/contour-ids.d.ts.map +1 -0
  41. package/dist/rules/contour-ids.js +12 -0
  42. package/dist/rules/contour-ids.js.map +1 -0
  43. package/dist/rules/cross-declarations.d.ts.map +1 -1
  44. package/dist/rules/cross-declarations.js +171 -57
  45. package/dist/rules/cross-declarations.js.map +1 -1
  46. package/dist/rules/dead-internal-trail.d.ts +3 -0
  47. package/dist/rules/dead-internal-trail.d.ts.map +1 -0
  48. package/dist/rules/dead-internal-trail.js +80 -0
  49. package/dist/rules/dead-internal-trail.js.map +1 -0
  50. package/dist/rules/draft-file-marking.d.ts +6 -0
  51. package/dist/rules/draft-file-marking.d.ts.map +1 -0
  52. package/dist/rules/draft-file-marking.js +87 -0
  53. package/dist/rules/draft-file-marking.js.map +1 -0
  54. package/dist/rules/draft-visible-debt.d.ts +12 -0
  55. package/dist/rules/draft-visible-debt.d.ts.map +1 -0
  56. package/dist/rules/draft-visible-debt.js +50 -0
  57. package/dist/rules/draft-visible-debt.js.map +1 -0
  58. package/dist/rules/error-mapping-completeness.d.ts +13 -0
  59. package/dist/rules/error-mapping-completeness.d.ts.map +1 -0
  60. package/dist/rules/error-mapping-completeness.js +160 -0
  61. package/dist/rules/error-mapping-completeness.js.map +1 -0
  62. package/dist/rules/example-valid.d.ts +6 -0
  63. package/dist/rules/example-valid.d.ts.map +1 -0
  64. package/dist/rules/example-valid.js +203 -0
  65. package/dist/rules/example-valid.js.map +1 -0
  66. package/dist/rules/fires-declarations.d.ts +16 -0
  67. package/dist/rules/fires-declarations.d.ts.map +1 -0
  68. package/dist/rules/fires-declarations.js +444 -0
  69. package/dist/rules/fires-declarations.js.map +1 -0
  70. package/dist/rules/implementation-returns-result.d.ts +9 -0
  71. package/dist/rules/implementation-returns-result.d.ts.map +1 -1
  72. package/dist/rules/implementation-returns-result.js +638 -76
  73. package/dist/rules/implementation-returns-result.js.map +1 -1
  74. package/dist/rules/incomplete-accessor-for-standard-op.d.ts +30 -0
  75. package/dist/rules/incomplete-accessor-for-standard-op.d.ts.map +1 -0
  76. package/dist/rules/incomplete-accessor-for-standard-op.js +226 -0
  77. package/dist/rules/incomplete-accessor-for-standard-op.js.map +1 -0
  78. package/dist/rules/incomplete-crud.d.ts +21 -0
  79. package/dist/rules/incomplete-crud.d.ts.map +1 -0
  80. package/dist/rules/incomplete-crud.js +368 -0
  81. package/dist/rules/incomplete-crud.js.map +1 -0
  82. package/dist/rules/index.d.ts +40 -7
  83. package/dist/rules/index.d.ts.map +1 -1
  84. package/dist/rules/index.js +91 -15
  85. package/dist/rules/index.js.map +1 -1
  86. package/dist/rules/intent-propagation.d.ts +3 -0
  87. package/dist/rules/intent-propagation.d.ts.map +1 -0
  88. package/dist/rules/intent-propagation.js +57 -0
  89. package/dist/rules/intent-propagation.js.map +1 -0
  90. package/dist/rules/missing-reconcile.d.ts +3 -0
  91. package/dist/rules/missing-reconcile.d.ts.map +1 -0
  92. package/dist/rules/missing-reconcile.js +44 -0
  93. package/dist/rules/missing-reconcile.js.map +1 -0
  94. package/dist/rules/missing-visibility.d.ts +3 -0
  95. package/dist/rules/missing-visibility.d.ts.map +1 -0
  96. package/dist/rules/missing-visibility.js +63 -0
  97. package/dist/rules/missing-visibility.js.map +1 -0
  98. package/dist/rules/no-direct-impl-in-route.d.ts.map +1 -1
  99. package/dist/rules/no-direct-impl-in-route.js +0 -3
  100. package/dist/rules/no-direct-impl-in-route.js.map +1 -1
  101. package/dist/rules/no-direct-implementation-call.js +1 -1
  102. package/dist/rules/no-direct-implementation-call.js.map +1 -1
  103. package/dist/rules/no-sync-result-assumption.d.ts.map +1 -1
  104. package/dist/rules/no-sync-result-assumption.js +870 -61
  105. package/dist/rules/no-sync-result-assumption.js.map +1 -1
  106. package/dist/rules/no-throw-in-detour-recover.d.ts +3 -0
  107. package/dist/rules/no-throw-in-detour-recover.d.ts.map +1 -0
  108. package/dist/rules/no-throw-in-detour-recover.js +147 -0
  109. package/dist/rules/no-throw-in-detour-recover.js.map +1 -0
  110. package/dist/rules/no-throw-in-detour-target.d.ts +4 -1
  111. package/dist/rules/no-throw-in-detour-target.d.ts.map +1 -1
  112. package/dist/rules/no-throw-in-detour-target.js +6 -3
  113. package/dist/rules/no-throw-in-detour-target.js.map +1 -1
  114. package/dist/rules/no-throw-in-implementation.d.ts +4 -2
  115. package/dist/rules/no-throw-in-implementation.d.ts.map +1 -1
  116. package/dist/rules/no-throw-in-implementation.js +6 -4
  117. package/dist/rules/no-throw-in-implementation.js.map +1 -1
  118. package/dist/rules/on-references-exist.d.ts +14 -0
  119. package/dist/rules/on-references-exist.d.ts.map +1 -0
  120. package/dist/rules/on-references-exist.js +109 -0
  121. package/dist/rules/on-references-exist.js.map +1 -0
  122. package/dist/rules/orphaned-signal.d.ts +3 -0
  123. package/dist/rules/orphaned-signal.d.ts.map +1 -0
  124. package/dist/rules/orphaned-signal.js +67 -0
  125. package/dist/rules/orphaned-signal.js.map +1 -0
  126. package/dist/rules/permit-governance.d.ts +3 -0
  127. package/dist/rules/permit-governance.d.ts.map +1 -0
  128. package/dist/rules/permit-governance.js +15 -0
  129. package/dist/rules/permit-governance.js.map +1 -0
  130. package/dist/rules/reference-exists.d.ts +6 -0
  131. package/dist/rules/reference-exists.d.ts.map +1 -0
  132. package/dist/rules/reference-exists.js +47 -0
  133. package/dist/rules/reference-exists.js.map +1 -0
  134. package/dist/rules/registry-names.d.ts +8 -0
  135. package/dist/rules/registry-names.d.ts.map +1 -0
  136. package/dist/rules/registry-names.js +83 -0
  137. package/dist/rules/registry-names.js.map +1 -0
  138. package/dist/rules/resource-declarations.d.ts +14 -0
  139. package/dist/rules/resource-declarations.d.ts.map +1 -0
  140. package/dist/rules/resource-declarations.js +413 -0
  141. package/dist/rules/resource-declarations.js.map +1 -0
  142. package/dist/rules/resource-exists.d.ts +6 -0
  143. package/dist/rules/resource-exists.d.ts.map +1 -0
  144. package/dist/rules/resource-exists.js +90 -0
  145. package/dist/rules/resource-exists.js.map +1 -0
  146. package/dist/rules/resource-id-grammar.d.ts +3 -0
  147. package/dist/rules/resource-id-grammar.d.ts.map +1 -0
  148. package/dist/rules/resource-id-grammar.js +39 -0
  149. package/dist/rules/resource-id-grammar.js.map +1 -0
  150. package/dist/rules/specs.d.ts.map +1 -1
  151. package/dist/rules/specs.js +5 -1
  152. package/dist/rules/specs.js.map +1 -1
  153. package/dist/rules/types.d.ts +53 -4
  154. package/dist/rules/types.d.ts.map +1 -1
  155. package/dist/rules/unreachable-detour-shadowing.d.ts +3 -0
  156. package/dist/rules/unreachable-detour-shadowing.d.ts.map +1 -0
  157. package/dist/rules/unreachable-detour-shadowing.js +202 -0
  158. package/dist/rules/unreachable-detour-shadowing.js.map +1 -0
  159. package/dist/rules/valid-describe-refs.d.ts.map +1 -1
  160. package/dist/rules/valid-describe-refs.js +132 -16
  161. package/dist/rules/valid-describe-refs.js.map +1 -1
  162. package/dist/rules/valid-detour-contract.d.ts +3 -0
  163. package/dist/rules/valid-detour-contract.d.ts.map +1 -0
  164. package/dist/rules/valid-detour-contract.js +47 -0
  165. package/dist/rules/valid-detour-contract.js.map +1 -0
  166. package/dist/rules/valid-detour-refs.d.ts.map +1 -1
  167. package/dist/rules/valid-detour-refs.js +73 -82
  168. package/dist/rules/valid-detour-refs.js.map +1 -1
  169. package/dist/rules/warden-export-symmetry.d.ts +7 -0
  170. package/dist/rules/warden-export-symmetry.d.ts.map +1 -0
  171. package/dist/rules/warden-export-symmetry.js +352 -0
  172. package/dist/rules/warden-export-symmetry.js.map +1 -0
  173. package/dist/rules/warden-rules-use-ast.d.ts +17 -0
  174. package/dist/rules/warden-rules-use-ast.d.ts.map +1 -0
  175. package/dist/rules/warden-rules-use-ast.js +778 -0
  176. package/dist/rules/warden-rules-use-ast.js.map +1 -0
  177. package/dist/trails/circular-refs.trail.d.ts +24 -0
  178. package/dist/trails/circular-refs.trail.d.ts.map +1 -0
  179. package/dist/trails/circular-refs.trail.js +29 -0
  180. package/dist/trails/circular-refs.trail.js.map +1 -0
  181. package/dist/trails/context-no-surface-types.trail.d.ts +2 -2
  182. package/dist/trails/context-no-surface-types.trail.d.ts.map +1 -1
  183. package/dist/trails/context-no-trailhead-types.trail.d.ts +2 -2
  184. package/dist/trails/context-no-trailhead-types.trail.d.ts.map +1 -1
  185. package/dist/trails/contour-exists.trail.d.ts +24 -0
  186. package/dist/trails/contour-exists.trail.d.ts.map +1 -0
  187. package/dist/trails/contour-exists.trail.js +21 -0
  188. package/dist/trails/contour-exists.trail.js.map +1 -0
  189. package/dist/trails/cross-declarations.trail.d.ts +2 -2
  190. package/dist/trails/cross-declarations.trail.d.ts.map +1 -1
  191. package/dist/trails/dead-internal-trail.trail.d.ts +24 -0
  192. package/dist/trails/dead-internal-trail.trail.d.ts.map +1 -0
  193. package/dist/trails/dead-internal-trail.trail.js +26 -0
  194. package/dist/trails/dead-internal-trail.trail.js.map +1 -0
  195. package/dist/trails/{provision-declarations.trail.d.ts → draft-file-marking.trail.d.ts} +3 -3
  196. package/dist/trails/draft-file-marking.trail.d.ts.map +1 -0
  197. package/dist/trails/draft-file-marking.trail.js +16 -0
  198. package/dist/trails/draft-file-marking.trail.js.map +1 -0
  199. package/dist/trails/draft-visible-debt.trail.d.ts +13 -0
  200. package/dist/trails/draft-visible-debt.trail.d.ts.map +1 -0
  201. package/dist/trails/draft-visible-debt.trail.js +16 -0
  202. package/dist/trails/draft-visible-debt.trail.js.map +1 -0
  203. package/dist/trails/error-mapping-completeness.trail.d.ts +13 -0
  204. package/dist/trails/error-mapping-completeness.trail.d.ts.map +1 -0
  205. package/dist/trails/error-mapping-completeness.trail.js +29 -0
  206. package/dist/trails/error-mapping-completeness.trail.js.map +1 -0
  207. package/dist/trails/{follow-declarations.trail.d.ts → example-valid.trail.d.ts} +3 -3
  208. package/dist/trails/example-valid.trail.d.ts.map +1 -0
  209. package/dist/trails/example-valid.trail.js +25 -0
  210. package/dist/trails/example-valid.trail.js.map +1 -0
  211. package/dist/trails/fires-declarations.trail.d.ts +13 -0
  212. package/dist/trails/fires-declarations.trail.d.ts.map +1 -0
  213. package/dist/trails/fires-declarations.trail.js +22 -0
  214. package/dist/trails/fires-declarations.trail.js.map +1 -0
  215. package/dist/trails/implementation-returns-result.trail.d.ts +2 -2
  216. package/dist/trails/implementation-returns-result.trail.d.ts.map +1 -1
  217. package/dist/trails/incomplete-accessor-for-standard-op.trail.d.ts +12 -0
  218. package/dist/trails/incomplete-accessor-for-standard-op.trail.d.ts.map +1 -0
  219. package/dist/trails/incomplete-accessor-for-standard-op.trail.js +60 -0
  220. package/dist/trails/incomplete-accessor-for-standard-op.trail.js.map +1 -0
  221. package/dist/trails/incomplete-crud.trail.d.ts +24 -0
  222. package/dist/trails/incomplete-crud.trail.d.ts.map +1 -0
  223. package/dist/trails/incomplete-crud.trail.js +39 -0
  224. package/dist/trails/incomplete-crud.trail.js.map +1 -0
  225. package/dist/trails/index.d.ts +29 -7
  226. package/dist/trails/index.d.ts.map +1 -1
  227. package/dist/trails/index.js +28 -6
  228. package/dist/trails/index.js.map +1 -1
  229. package/dist/trails/intent-propagation.trail.d.ts +24 -0
  230. package/dist/trails/intent-propagation.trail.d.ts.map +1 -0
  231. package/dist/trails/intent-propagation.trail.js +30 -0
  232. package/dist/trails/intent-propagation.trail.js.map +1 -0
  233. package/dist/trails/missing-reconcile.trail.d.ts +24 -0
  234. package/dist/trails/missing-reconcile.trail.d.ts.map +1 -0
  235. package/dist/trails/missing-reconcile.trail.js +33 -0
  236. package/dist/trails/missing-reconcile.trail.js.map +1 -0
  237. package/dist/trails/missing-visibility.trail.d.ts +24 -0
  238. package/dist/trails/missing-visibility.trail.d.ts.map +1 -0
  239. package/dist/trails/missing-visibility.trail.js +22 -0
  240. package/dist/trails/missing-visibility.trail.js.map +1 -0
  241. package/dist/trails/no-direct-impl-in-route.trail.d.ts +2 -2
  242. package/dist/trails/no-direct-impl-in-route.trail.d.ts.map +1 -1
  243. package/dist/trails/no-direct-implementation-call.trail.d.ts +2 -2
  244. package/dist/trails/no-direct-implementation-call.trail.d.ts.map +1 -1
  245. package/dist/trails/no-sync-result-assumption.trail.d.ts +2 -2
  246. package/dist/trails/no-sync-result-assumption.trail.d.ts.map +1 -1
  247. package/dist/trails/no-throw-in-detour-recover.trail.d.ts +13 -0
  248. package/dist/trails/no-throw-in-detour-recover.trail.d.ts.map +1 -0
  249. package/dist/trails/no-throw-in-detour-recover.trail.js +24 -0
  250. package/dist/trails/no-throw-in-detour-recover.trail.js.map +1 -0
  251. package/dist/trails/no-throw-in-detour-target.trail.d.ts +13 -3
  252. package/dist/trails/no-throw-in-detour-target.trail.d.ts.map +1 -1
  253. package/dist/trails/no-throw-in-implementation.trail.d.ts +2 -2
  254. package/dist/trails/no-throw-in-implementation.trail.d.ts.map +1 -1
  255. package/dist/trails/on-references-exist.trail.d.ts +24 -0
  256. package/dist/trails/on-references-exist.trail.d.ts.map +1 -0
  257. package/dist/trails/on-references-exist.trail.js +21 -0
  258. package/dist/trails/on-references-exist.trail.js.map +1 -0
  259. package/dist/trails/orphaned-signal.trail.d.ts +24 -0
  260. package/dist/trails/orphaned-signal.trail.d.ts.map +1 -0
  261. package/dist/trails/orphaned-signal.trail.js +36 -0
  262. package/dist/trails/orphaned-signal.trail.js.map +1 -0
  263. package/dist/trails/permit-governance.trail.d.ts +12 -0
  264. package/dist/trails/permit-governance.trail.d.ts.map +1 -0
  265. package/dist/trails/permit-governance.trail.js +47 -0
  266. package/dist/trails/permit-governance.trail.js.map +1 -0
  267. package/dist/trails/prefer-schema-inference.trail.d.ts +2 -2
  268. package/dist/trails/prefer-schema-inference.trail.d.ts.map +1 -1
  269. package/dist/trails/reference-exists.trail.d.ts +24 -0
  270. package/dist/trails/reference-exists.trail.d.ts.map +1 -0
  271. package/dist/trails/reference-exists.trail.js +25 -0
  272. package/dist/trails/reference-exists.trail.js.map +1 -0
  273. package/dist/trails/resource-declarations.trail.d.ts +13 -0
  274. package/dist/trails/resource-declarations.trail.d.ts.map +1 -0
  275. package/dist/trails/{provision-declarations.trail.js → resource-declarations.trail.js} +7 -7
  276. package/dist/trails/resource-declarations.trail.js.map +1 -0
  277. package/dist/trails/resource-exists.trail.d.ts +24 -0
  278. package/dist/trails/resource-exists.trail.d.ts.map +1 -0
  279. package/dist/trails/{provision-exists.trail.js → resource-exists.trail.js} +8 -8
  280. package/dist/trails/resource-exists.trail.js.map +1 -0
  281. package/dist/trails/resource-id-grammar.trail.d.ts +13 -0
  282. package/dist/trails/resource-id-grammar.trail.d.ts.map +1 -0
  283. package/dist/trails/resource-id-grammar.trail.js +38 -0
  284. package/dist/trails/resource-id-grammar.trail.js.map +1 -0
  285. package/dist/trails/run.d.ts +25 -9
  286. package/dist/trails/run.d.ts.map +1 -1
  287. package/dist/trails/run.js +63 -19
  288. package/dist/trails/run.js.map +1 -1
  289. package/dist/trails/schema.d.ts +28 -3
  290. package/dist/trails/schema.d.ts.map +1 -1
  291. package/dist/trails/schema.js +57 -4
  292. package/dist/trails/schema.js.map +1 -1
  293. package/dist/trails/unreachable-detour-shadowing.trail.d.ts +13 -0
  294. package/dist/trails/unreachable-detour-shadowing.trail.d.ts.map +1 -0
  295. package/dist/trails/unreachable-detour-shadowing.trail.js +44 -0
  296. package/dist/trails/unreachable-detour-shadowing.trail.js.map +1 -0
  297. package/dist/trails/valid-describe-refs.trail.d.ts +12 -3
  298. package/dist/trails/valid-describe-refs.trail.d.ts.map +1 -1
  299. package/dist/trails/valid-detour-contract.trail.d.ts +12 -0
  300. package/dist/trails/valid-detour-contract.trail.d.ts.map +1 -0
  301. package/dist/trails/valid-detour-contract.trail.js +66 -0
  302. package/dist/trails/valid-detour-contract.trail.js.map +1 -0
  303. package/dist/trails/valid-detour-refs.trail.d.ts +13 -3
  304. package/dist/trails/valid-detour-refs.trail.d.ts.map +1 -1
  305. package/dist/trails/warden-export-symmetry.trail.d.ts +13 -0
  306. package/dist/trails/warden-export-symmetry.trail.d.ts.map +1 -0
  307. package/dist/trails/warden-export-symmetry.trail.js +16 -0
  308. package/dist/trails/warden-export-symmetry.trail.js.map +1 -0
  309. package/dist/trails/warden-rules-use-ast.trail.d.ts +13 -0
  310. package/dist/trails/warden-rules-use-ast.trail.d.ts.map +1 -0
  311. package/dist/trails/warden-rules-use-ast.trail.js +41 -0
  312. package/dist/trails/warden-rules-use-ast.trail.js.map +1 -0
  313. package/dist/trails/wrap-rule.d.ts +16 -2
  314. package/dist/trails/wrap-rule.d.ts.map +1 -1
  315. package/dist/trails/wrap-rule.js +71 -11
  316. package/dist/trails/wrap-rule.js.map +1 -1
  317. package/package.json +7 -4
  318. package/src/__tests__/ast.test.ts +613 -0
  319. package/src/__tests__/circular-refs.test.ts +121 -0
  320. package/src/__tests__/cli.test.ts +360 -32
  321. package/src/__tests__/contour-exists.test.ts +203 -0
  322. package/src/__tests__/cross-declarations.test.ts +245 -0
  323. package/src/__tests__/dead-internal-trail.test.ts +81 -0
  324. package/src/__tests__/draft-rules-context.test.ts +150 -0
  325. package/src/__tests__/drift.test.ts +75 -5
  326. package/src/__tests__/error-mapping-completeness.test.ts +56 -0
  327. package/src/__tests__/example-valid.test.ts +101 -0
  328. package/src/__tests__/fires-declarations-param-destructure.test.ts +54 -0
  329. package/src/__tests__/fires-declarations.test.ts +652 -0
  330. package/src/__tests__/formatters.test.ts +2 -2
  331. package/src/__tests__/implementation-returns-result.test.ts +1016 -2
  332. package/src/__tests__/incomplete-accessor-for-standard-op.test.ts +337 -0
  333. package/src/__tests__/incomplete-crud.test.ts +498 -0
  334. package/src/__tests__/intent-propagation.test.ts +116 -0
  335. package/src/__tests__/missing-reconcile.test.ts +154 -0
  336. package/src/__tests__/missing-visibility.test.ts +108 -0
  337. package/src/__tests__/no-sync-result-assumption.test.ts +870 -39
  338. package/src/__tests__/no-throw-in-detour-recover.test.ts +93 -0
  339. package/src/__tests__/no-throw-in-implementation.test.ts +88 -0
  340. package/src/__tests__/on-references-exist.test.ts +151 -0
  341. package/src/__tests__/orphaned-signal.test.ts +137 -0
  342. package/src/__tests__/permit-governance.test.ts +66 -0
  343. package/src/__tests__/reference-exists.test.ts +281 -0
  344. package/src/__tests__/resource-declarations.test.ts +448 -0
  345. package/src/__tests__/resource-exists.test.ts +122 -0
  346. package/src/__tests__/resource-id-grammar.test.ts +50 -0
  347. package/src/__tests__/rules.test.ts +17 -77
  348. package/src/__tests__/topo-aware-rule.test.ts +257 -0
  349. package/src/__tests__/trails.test.ts +2 -2
  350. package/src/__tests__/unreachable-detour-shadowing.test.ts +128 -0
  351. package/src/__tests__/valid-describe-refs.test.ts +183 -0
  352. package/src/__tests__/valid-detour-contract.test.ts +86 -0
  353. package/src/__tests__/warden-export-symmetry.test.ts +251 -0
  354. package/src/__tests__/warden-rules-use-ast.test.ts +468 -0
  355. package/src/__tests__/wrap-rule.test.ts +3 -3
  356. package/src/cli.ts +458 -91
  357. package/src/draft.ts +22 -0
  358. package/src/drift.ts +63 -21
  359. package/src/formatters.ts +15 -4
  360. package/src/index.ts +62 -23
  361. package/src/rules/ast.ts +2715 -119
  362. package/src/rules/circular-refs.ts +154 -0
  363. package/src/rules/{context-no-trailhead-types.ts → context-no-surface-types.ts} +72 -12
  364. package/src/rules/contour-exists.ts +251 -0
  365. package/src/rules/contour-ids.ts +15 -0
  366. package/src/rules/cross-declarations.ts +277 -69
  367. package/src/rules/dead-internal-trail.ts +141 -0
  368. package/src/rules/draft-file-marking.ts +160 -0
  369. package/src/rules/draft-visible-debt.ts +87 -0
  370. package/src/rules/error-mapping-completeness.ts +273 -0
  371. package/src/rules/example-valid.ts +401 -0
  372. package/src/rules/fires-declarations.ts +609 -0
  373. package/src/rules/implementation-returns-result.ts +1042 -122
  374. package/src/rules/incomplete-accessor-for-standard-op.ts +315 -0
  375. package/src/rules/incomplete-crud.ts +579 -0
  376. package/src/rules/index.ts +95 -16
  377. package/src/rules/intent-propagation.ts +142 -0
  378. package/src/rules/missing-reconcile.ts +98 -0
  379. package/src/rules/missing-visibility.ts +110 -0
  380. package/src/rules/no-direct-impl-in-route.ts +0 -4
  381. package/src/rules/no-direct-implementation-call.ts +1 -1
  382. package/src/rules/no-sync-result-assumption.ts +1134 -96
  383. package/src/rules/no-throw-in-detour-recover.ts +225 -0
  384. package/src/rules/no-throw-in-implementation.ts +6 -4
  385. package/src/rules/on-references-exist.ts +194 -0
  386. package/src/rules/orphaned-signal.ts +150 -0
  387. package/src/rules/permit-governance.ts +25 -0
  388. package/src/rules/reference-exists.ts +98 -0
  389. package/src/rules/registry-names.ts +83 -0
  390. package/src/rules/{provision-declarations.ts → resource-declarations.ts} +208 -138
  391. package/src/rules/{provision-exists.ts → resource-exists.ts} +48 -51
  392. package/src/rules/resource-id-grammar.ts +65 -0
  393. package/src/rules/specs.ts +5 -1
  394. package/src/rules/types.ts +57 -4
  395. package/src/rules/unreachable-detour-shadowing.ts +375 -0
  396. package/src/rules/valid-describe-refs.ts +160 -32
  397. package/src/rules/valid-detour-contract.ts +78 -0
  398. package/src/rules/warden-export-symmetry.ts +533 -0
  399. package/src/rules/warden-rules-use-ast.ts +996 -0
  400. package/src/trails/circular-refs.trail.ts +29 -0
  401. package/src/trails/{context-no-trailhead-types.trail.ts → context-no-surface-types.trail.ts} +4 -4
  402. package/src/trails/contour-exists.trail.ts +21 -0
  403. package/src/trails/dead-internal-trail.trail.ts +26 -0
  404. package/src/trails/draft-file-marking.trail.ts +16 -0
  405. package/src/trails/draft-visible-debt.trail.ts +16 -0
  406. package/src/trails/error-mapping-completeness.trail.ts +29 -0
  407. package/src/trails/example-valid.trail.ts +25 -0
  408. package/src/trails/fires-declarations.trail.ts +22 -0
  409. package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
  410. package/src/trails/incomplete-crud.trail.ts +39 -0
  411. package/src/trails/index.ts +40 -7
  412. package/src/trails/intent-propagation.trail.ts +30 -0
  413. package/src/trails/missing-reconcile.trail.ts +33 -0
  414. package/src/trails/missing-visibility.trail.ts +22 -0
  415. package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
  416. package/src/trails/on-references-exist.trail.ts +21 -0
  417. package/src/trails/orphaned-signal.trail.ts +36 -0
  418. package/src/trails/permit-governance.trail.ts +51 -0
  419. package/src/trails/reference-exists.trail.ts +25 -0
  420. package/src/trails/{provision-declarations.trail.ts → resource-declarations.trail.ts} +6 -6
  421. package/src/trails/{provision-exists.trail.ts → resource-exists.trail.ts} +7 -7
  422. package/src/trails/resource-id-grammar.trail.ts +39 -0
  423. package/src/trails/run.ts +121 -24
  424. package/src/trails/schema.ts +66 -4
  425. package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
  426. package/src/trails/valid-detour-contract.trail.ts +71 -0
  427. package/src/trails/warden-export-symmetry.trail.ts +16 -0
  428. package/src/trails/warden-rules-use-ast.trail.ts +45 -0
  429. package/src/trails/wrap-rule.ts +104 -12
  430. package/tsconfig.tests.json +10 -0
  431. package/tsconfig.tsbuildinfo +1 -1
  432. package/dist/rules/follow-declarations.d.ts +0 -13
  433. package/dist/rules/follow-declarations.d.ts.map +0 -1
  434. package/dist/rules/follow-declarations.js +0 -264
  435. package/dist/rules/follow-declarations.js.map +0 -1
  436. package/dist/rules/provision-declarations.d.ts +0 -14
  437. package/dist/rules/provision-declarations.d.ts.map +0 -1
  438. package/dist/rules/provision-declarations.js +0 -344
  439. package/dist/rules/provision-declarations.js.map +0 -1
  440. package/dist/rules/provision-exists.d.ts +0 -6
  441. package/dist/rules/provision-exists.d.ts.map +0 -1
  442. package/dist/rules/provision-exists.js +0 -89
  443. package/dist/rules/provision-exists.js.map +0 -1
  444. package/dist/rules/service-declarations.d.ts +0 -16
  445. package/dist/rules/service-declarations.d.ts.map +0 -1
  446. package/dist/rules/service-declarations.js +0 -346
  447. package/dist/rules/service-declarations.js.map +0 -1
  448. package/dist/rules/service-exists.d.ts +0 -8
  449. package/dist/rules/service-exists.d.ts.map +0 -1
  450. package/dist/rules/service-exists.js +0 -91
  451. package/dist/rules/service-exists.js.map +0 -1
  452. package/dist/trails/follow-declarations.trail.d.ts.map +0 -1
  453. package/dist/trails/follow-declarations.trail.js +0 -22
  454. package/dist/trails/follow-declarations.trail.js.map +0 -1
  455. package/dist/trails/provision-declarations.trail.d.ts.map +0 -1
  456. package/dist/trails/provision-declarations.trail.js.map +0 -1
  457. package/dist/trails/provision-exists.trail.d.ts +0 -15
  458. package/dist/trails/provision-exists.trail.d.ts.map +0 -1
  459. package/dist/trails/provision-exists.trail.js.map +0 -1
  460. package/dist/trails/service-declarations.trail.d.ts +0 -26
  461. package/dist/trails/service-declarations.trail.d.ts.map +0 -1
  462. package/dist/trails/service-declarations.trail.js +0 -27
  463. package/dist/trails/service-declarations.trail.js.map +0 -1
  464. package/dist/trails/service-exists.trail.d.ts +0 -32
  465. package/dist/trails/service-exists.trail.d.ts.map +0 -1
  466. package/dist/trails/service-exists.trail.js +0 -29
  467. package/dist/trails/service-exists.trail.js.map +0 -1
  468. package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
  469. package/src/__tests__/provision-declarations.test.ts +0 -318
  470. package/src/__tests__/provision-exists.test.ts +0 -122
  471. package/src/rules/no-throw-in-detour-target.ts +0 -150
  472. package/src/rules/valid-detour-refs.ts +0 -187
  473. package/src/trails/no-throw-in-detour-target.trail.ts +0 -20
  474. package/src/trails/valid-detour-refs.trail.ts +0 -24
@@ -0,0 +1,121 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import { circularRefs } from '../rules/circular-refs.js';
4
+
5
+ const TEST_FILE = 'contours.ts';
6
+
7
+ describe('circular-refs', () => {
8
+ test('passes when contour references are acyclic', () => {
9
+ const code = `
10
+ import { contour } from '@ontrails/core';
11
+ import { z } from 'zod';
12
+
13
+ const user = contour('user', {
14
+ id: z.string().uuid(),
15
+ }, { identity: 'id' });
16
+
17
+ const gist = contour('gist', {
18
+ id: z.string().uuid(),
19
+ ownerId: user.id(),
20
+ }, { identity: 'id' });
21
+ `;
22
+
23
+ expect(circularRefs.check(code, TEST_FILE)).toEqual([]);
24
+ });
25
+
26
+ test('warns on direct local contour cycles', () => {
27
+ const code = `
28
+ import { contour } from '@ontrails/core';
29
+ import { z } from 'zod';
30
+
31
+ const user = contour('user', {
32
+ gistId: gist.id(),
33
+ id: z.string().uuid(),
34
+ }, { identity: 'id' });
35
+
36
+ const gist = contour('gist', {
37
+ id: z.string().uuid(),
38
+ ownerId: user.id(),
39
+ }, { identity: 'id' });
40
+ `;
41
+
42
+ const diagnostics = circularRefs.check(code, TEST_FILE);
43
+
44
+ expect(diagnostics).toHaveLength(2);
45
+ expect(diagnostics[0]?.rule).toBe('circular-refs');
46
+ expect(diagnostics[0]?.message).toContain('user -> gist -> user');
47
+ });
48
+
49
+ test('warns on transitive cycles discovered through project context', () => {
50
+ const code = `
51
+ import { contour } from '@ontrails/core';
52
+ import { z } from 'zod';
53
+ import { gist } from './gist';
54
+
55
+ const user = contour('user', {
56
+ gistId: gist.id(),
57
+ id: z.string().uuid(),
58
+ }, { identity: 'id' });
59
+ `;
60
+
61
+ const diagnostics = circularRefs.checkWithContext(code, TEST_FILE, {
62
+ contourReferencesByName: new Map([
63
+ ['account', ['user']],
64
+ ['gist', ['account']],
65
+ ]),
66
+ knownContourIds: new Set(['account', 'gist', 'user']),
67
+ knownTrailIds: new Set<string>(),
68
+ });
69
+
70
+ expect(diagnostics).toHaveLength(1);
71
+ expect(diagnostics[0]?.message).toContain(
72
+ 'user -> gist -> account -> user'
73
+ );
74
+ });
75
+
76
+ test('warns on local cycles formed through wrapped contour id schemas', () => {
77
+ const code = `
78
+ import { contour } from '@ontrails/core';
79
+ import { z } from 'zod';
80
+
81
+ const user = contour('user', {
82
+ gistId: gist.id().optional(),
83
+ id: z.string().uuid(),
84
+ }, { identity: 'id' });
85
+
86
+ const gist = contour('gist', {
87
+ id: z.string().uuid(),
88
+ ownerId: user.id().nullable(),
89
+ }, { identity: 'id' });
90
+ `;
91
+
92
+ const diagnostics = circularRefs.check(code, TEST_FILE);
93
+
94
+ expect(diagnostics).toHaveLength(2);
95
+ expect(diagnostics[0]?.rule).toBe('circular-refs');
96
+ expect(diagnostics[0]?.message).toContain('user -> gist -> user');
97
+ });
98
+
99
+ test('warns on local cycles formed through namespace-imported references', () => {
100
+ const code = `
101
+ import { contour } from '@ontrails/core';
102
+ import * as contours from './contours';
103
+
104
+ const user = contour('user', {
105
+ id: 'x',
106
+ gistId: contours.gist.id(),
107
+ }, { identity: 'id' });
108
+
109
+ const gist = contour('gist', {
110
+ id: 'x',
111
+ ownerId: contours.user.id(),
112
+ }, { identity: 'id' });
113
+ `;
114
+
115
+ const diagnostics = circularRefs.check(code, TEST_FILE);
116
+
117
+ expect(diagnostics).toHaveLength(2);
118
+ expect(diagnostics[0]?.rule).toBe('circular-refs');
119
+ expect(diagnostics[0]?.message).toContain('user -> gist -> user');
120
+ });
121
+ });
@@ -3,8 +3,29 @@ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir } from 'node:os';
4
4
  import { join } from 'node:path';
5
5
 
6
+ import { ConflictError, Result, topo, trail } from '@ontrails/core';
7
+ import { z } from 'zod';
8
+
6
9
  import { formatWardenReport, runWarden } from '../cli.js';
7
10
 
11
+ const isDraftFileMarking = (rule: string): boolean =>
12
+ rule === 'draft-file-marking';
13
+
14
+ const isDraftFileMarkingError = (diagnostic: {
15
+ rule: string;
16
+ severity?: string;
17
+ }): boolean =>
18
+ isDraftFileMarking(diagnostic.rule) && diagnostic.severity === 'error';
19
+
20
+ const isDraftFileMarkingWarn = (diagnostic: {
21
+ rule: string;
22
+ severity?: string;
23
+ }): boolean =>
24
+ isDraftFileMarking(diagnostic.rule) && diagnostic.severity === 'warn';
25
+
26
+ const isDraftVisibleDebt = (rule: string): boolean =>
27
+ rule === 'draft-visible-debt';
28
+
8
29
  const makeTempDir = (): string => {
9
30
  const dir = join(
10
31
  tmpdir(),
@@ -14,7 +35,7 @@ const makeTempDir = (): string => {
14
35
  return dir;
15
36
  };
16
37
 
17
- describe('runWarden', () => {
38
+ describe('runWarden basics', () => {
18
39
  test('produces a report with diagnostics for bad code', async () => {
19
40
  const dir = makeTempDir();
20
41
  try {
@@ -81,67 +102,356 @@ describe('runWarden', () => {
81
102
  rmSync(dir, { force: true, recursive: true });
82
103
  }
83
104
  });
105
+ });
84
106
 
85
- test('uses project context for detour references across files', async () => {
107
+ describe('runWarden project context', () => {
108
+ test('reports throw inside detour recover functions', async () => {
86
109
  const dir = makeTempDir();
87
110
  try {
88
111
  writeFileSync(
89
- join(dir, 'show.ts'),
112
+ join(dir, 'recover.ts'),
90
113
  `trail("entity.show", {
91
- detours: { NotFoundError: ["entity.search"] },
92
- blaze: async (input, ctx) => {
93
- return Result.ok(data);
94
- }
114
+ detours: [
115
+ {
116
+ on: ConflictError,
117
+ recover: async () => {
118
+ throw new Error("boom");
119
+ },
120
+ },
121
+ ],
122
+ blaze: () => Result.err(new ConflictError("conflict")),
95
123
  })`
96
124
  );
125
+
126
+ const report = await runWarden({ rootDir: dir });
127
+ const detourRecoverErrors = report.diagnostics.filter(
128
+ (diagnostic) => diagnostic.rule === 'no-throw-in-detour-recover'
129
+ );
130
+
131
+ expect(detourRecoverErrors).toHaveLength(1);
132
+ } finally {
133
+ rmSync(dir, { force: true, recursive: true });
134
+ }
135
+ });
136
+
137
+ test('reports detours shadowed by earlier broader on: types', async () => {
138
+ const dir = makeTempDir();
139
+ try {
97
140
  writeFileSync(
98
- join(dir, 'search.ts'),
99
- `trail("entity.search", {
100
- blaze: async (input, ctx) => {
101
- return Result.ok(data);
102
- }
141
+ join(dir, 'shadowed.ts'),
142
+ `import { ConflictError, Result, TrailsError, trail } from '@ontrails/core';
143
+
144
+ trail("entity.save", {
145
+ detours: [
146
+ { on: TrailsError, recover: async () => Result.ok({ winner: "broad" }) },
147
+ { on: ConflictError, recover: async () => Result.ok({ winner: "specific" }) },
148
+ ],
149
+ blaze: () => Result.err(new ConflictError("boom")),
150
+ });`
151
+ );
152
+
153
+ const report = await runWarden({ rootDir: dir });
154
+ const shadowRules = report.diagnostics.filter(
155
+ (diagnostic) => diagnostic.rule === 'unreachable-detour-shadowing'
156
+ );
157
+
158
+ expect(shadowRules).toHaveLength(1);
159
+ expect(shadowRules[0]?.message).toContain('TrailsError');
160
+ expect(shadowRules[0]?.message).toContain('ConflictError');
161
+ } finally {
162
+ rmSync(dir, { force: true, recursive: true });
163
+ }
164
+ });
165
+
166
+ test('includes topo-aware detour contract diagnostics when topo is supplied', async () => {
167
+ const validTrail = trail('entity.save', {
168
+ blaze: () => Result.ok({ ok: true }),
169
+ detours: [
170
+ {
171
+ on: ConflictError,
172
+ recover: () => Result.ok({ ok: true }),
173
+ },
174
+ ],
175
+ input: z.object({}),
176
+ output: z.object({ ok: z.boolean() }),
177
+ });
178
+
179
+ const malformed = {
180
+ ...validTrail,
181
+ detours: [
182
+ {
183
+ on: 'ConflictError',
184
+ recover: 'not callable',
185
+ },
186
+ ],
187
+ } as typeof validTrail;
188
+
189
+ const report = await runWarden({
190
+ topo: topo('invalid-detour-contract', {
191
+ malformed,
192
+ } as Record<string, unknown>),
193
+ });
194
+
195
+ const contractDiagnostics = report.diagnostics.filter(
196
+ (diagnostic) => diagnostic.rule === 'valid-detour-contract'
197
+ );
198
+
199
+ expect(contractDiagnostics).toHaveLength(2);
200
+ });
201
+
202
+ test('uses project context for contour references across files', async () => {
203
+ const dir = makeTempDir();
204
+ try {
205
+ writeFileSync(
206
+ join(dir, 'user.ts'),
207
+ `import { contour } from '@ontrails/core';
208
+ import { z } from 'zod';
209
+
210
+ export const user = contour('user', {
211
+ id: z.string().uuid(),
212
+ }, { identity: 'id' });`
213
+ );
214
+ writeFileSync(
215
+ join(dir, 'gist.ts'),
216
+ `import { contour } from '@ontrails/core';
217
+ import { z } from 'zod';
218
+ import { user } from './user';
219
+
220
+ export const gist = contour('gist', {
221
+ id: z.string().uuid(),
222
+ ownerId: user.id(),
223
+ }, { identity: 'id' });`
224
+ );
225
+
226
+ const report = await runWarden({ rootDir: dir });
227
+ const referenceErrors = report.diagnostics.filter(
228
+ (diagnostic) => diagnostic.rule === 'reference-exists'
229
+ );
230
+
231
+ expect(referenceErrors).toHaveLength(0);
232
+ } finally {
233
+ rmSync(dir, { force: true, recursive: true });
234
+ }
235
+ });
236
+
237
+ test('preserves empty topo resource sets instead of falling back to file-local ids', async () => {
238
+ const dir = makeTempDir();
239
+ try {
240
+ writeFileSync(
241
+ join(dir, 'entity.ts'),
242
+ `import { Result, resource, trail } from '@ontrails/core';
243
+
244
+ const db = resource('db.main', {
245
+ create: () => Result.ok({ source: 'factory' }),
246
+ });
247
+
248
+ trail('entity.show', {
249
+ resources: [db],
250
+ blaze: async (_input, ctx) => Result.ok(db.from(ctx)),
251
+ });`
252
+ );
253
+
254
+ const report = await runWarden({ rootDir: dir, topo: topo('empty-app') });
255
+ const resourceErrors = report.diagnostics.filter(
256
+ (diagnostic) => diagnostic.rule === 'resource-exists'
257
+ );
258
+
259
+ expect(resourceErrors).toHaveLength(1);
260
+ expect(resourceErrors[0]?.message).toContain('db.main');
261
+ } finally {
262
+ rmSync(dir, { force: true, recursive: true });
263
+ }
264
+ });
265
+
266
+ test('warns on contour cycles declared across files', async () => {
267
+ const dir = makeTempDir();
268
+ try {
269
+ writeFileSync(
270
+ join(dir, 'user.ts'),
271
+ `import { contour } from '@ontrails/core';
272
+ import { z } from 'zod';
273
+ import { gist } from './gist';
274
+
275
+ export const user = contour('user', {
276
+ gistId: gist.id(),
277
+ id: z.string().uuid(),
278
+ }, { identity: 'id' });`
279
+ );
280
+ writeFileSync(
281
+ join(dir, 'gist.ts'),
282
+ `import { contour } from '@ontrails/core';
283
+ import { z } from 'zod';
284
+ import { user } from './user';
285
+
286
+ export const gist = contour('gist', {
287
+ id: z.string().uuid(),
288
+ ownerId: user.id(),
289
+ }, { identity: 'id' });`
290
+ );
291
+
292
+ const report = await runWarden({ rootDir: dir });
293
+ const circularWarnings = report.diagnostics.filter(
294
+ (diagnostic) => diagnostic.rule === 'circular-refs'
295
+ );
296
+
297
+ expect(circularWarnings.length).toBeGreaterThan(0);
298
+ } finally {
299
+ rmSync(dir, { force: true, recursive: true });
300
+ }
301
+ });
302
+
303
+ test('uses project context for store factory completeness across files', async () => {
304
+ const dir = makeTempDir();
305
+ try {
306
+ writeFileSync(
307
+ join(dir, 'store.ts'),
308
+ `import { Result, resource } from '@ontrails/core';
309
+ import { store } from '@ontrails/store';
310
+ import { crud } from '@ontrails/store/trails';
311
+ import { z } from 'zod';
312
+
313
+ export const definition = store({
314
+ notes: {
315
+ identity: 'id',
316
+ schema: z.object({
317
+ id: z.string(),
318
+ title: z.string(),
319
+ }),
320
+ versioned: true,
321
+ },
322
+ });
323
+
324
+ const notesResource = resource('db.notes', {
325
+ create: () => Result.ok({}),
326
+ mock: () => ({}),
327
+ });
328
+
329
+ const noteTrails = crud(definition.tables.notes, notesResource);`
330
+ );
331
+ writeFileSync(
332
+ join(dir, 'reconcile.ts'),
333
+ `import { Result, resource } from '@ontrails/core';
334
+ import { reconcile } from '@ontrails/store/trails';
335
+ import { definition } from './store';
336
+
337
+ const notesResource = resource('db.notes', {
338
+ create: () => Result.ok({}),
339
+ mock: () => ({}),
340
+ });
341
+
342
+ const reconcileNotes = reconcile({
343
+ resource: notesResource,
344
+ table: definition.tables.notes,
345
+ });`
346
+ );
347
+ writeFileSync(
348
+ join(dir, 'listener.ts'),
349
+ `import { Result, trail } from '@ontrails/core';
350
+ import { definition } from './store';
351
+ import { z } from 'zod';
352
+
353
+ trail('notes.notify', {
354
+ on: [
355
+ definition.tables.notes.signals.created,
356
+ definition.tables.notes.signals.updated,
357
+ definition.tables.notes.signals.removed,
358
+ ],
359
+ blaze: async () => Result.ok({ ok: true }),
360
+ output: z.object({ ok: z.boolean() }),
361
+ });`
362
+ );
363
+
364
+ const report = await runWarden({ rootDir: dir });
365
+
366
+ expect(
367
+ report.diagnostics.filter(
368
+ (diagnostic) => diagnostic.rule === 'missing-reconcile'
369
+ )
370
+ ).toHaveLength(0);
371
+ expect(
372
+ report.diagnostics.filter(
373
+ (diagnostic) => diagnostic.rule === 'orphaned-signal'
374
+ )
375
+ ).toHaveLength(0);
376
+ } finally {
377
+ rmSync(dir, { force: true, recursive: true });
378
+ }
379
+ });
380
+ });
381
+
382
+ describe('runWarden draft markers', () => {
383
+ test('requires draft-bearing files to be visibly marked', async () => {
384
+ const dir = makeTempDir();
385
+ try {
386
+ writeFileSync(
387
+ join(dir, 'draft-id.ts'),
388
+ `trail("_draft.entity.prepare", {
389
+ blaze: async () => Result.ok({ ok: true }),
390
+ input: z.object({})
103
391
  })`
104
392
  );
105
393
 
106
394
  const report = await runWarden({ rootDir: dir });
107
- const detourRefErrors = report.diagnostics.filter(
108
- (diagnostic) => diagnostic.rule === 'valid-detour-refs'
395
+
396
+ const hasDraftFileMarking = report.diagnostics.some((diagnostic) =>
397
+ isDraftFileMarking(diagnostic.rule)
398
+ );
399
+ const hasDraftVisibleDebt = report.diagnostics.some((diagnostic) =>
400
+ isDraftVisibleDebt(diagnostic.rule)
109
401
  );
110
402
 
111
- expect(detourRefErrors).toHaveLength(0);
403
+ expect(hasDraftFileMarking).toBe(true);
404
+ expect(hasDraftVisibleDebt).toBe(true);
112
405
  } finally {
113
406
  rmSync(dir, { force: true, recursive: true });
114
407
  }
115
408
  });
116
409
 
117
- test('flags throws in detour targets declared in another file', async () => {
410
+ test('allows correctly marked draft files while keeping the debt visible', async () => {
118
411
  const dir = makeTempDir();
119
412
  try {
120
413
  writeFileSync(
121
- join(dir, 'show.ts'),
122
- `trail("entity.show", {
123
- detours: { NotFoundError: ["entity.search"] },
124
- blaze: async (input, ctx) => {
125
- return Result.ok(data);
126
- }
414
+ join(dir, '_draft.entity.ts'),
415
+ `trail("_draft.entity.prepare", {
416
+ blaze: async () => Result.ok({ ok: true }),
417
+ input: z.object({})
127
418
  })`
128
419
  );
420
+
421
+ const report = await runWarden({ rootDir: dir });
422
+
423
+ const hasDraftFileMarkingError = report.diagnostics.some((diagnostic) =>
424
+ isDraftFileMarkingError(diagnostic)
425
+ );
426
+ const hasDraftVisibleDebt = report.diagnostics.some((diagnostic) =>
427
+ isDraftVisibleDebt(diagnostic.rule)
428
+ );
429
+
430
+ expect(hasDraftFileMarkingError).toBe(false);
431
+ expect(hasDraftVisibleDebt).toBe(true);
432
+ } finally {
433
+ rmSync(dir, { force: true, recursive: true });
434
+ }
435
+ });
436
+
437
+ test('warns when a draft-marked file no longer contains draft ids', async () => {
438
+ const dir = makeTempDir();
439
+ try {
129
440
  writeFileSync(
130
- join(dir, 'search.ts'),
131
- `trail("entity.search", {
132
- blaze: async (input, ctx) => {
133
- throw new Error("boom");
134
- }
441
+ join(dir, 'entity.draft.ts'),
442
+ `trail("entity.prepare", {
443
+ blaze: async () => Result.ok({ ok: true }),
444
+ input: z.object({})
135
445
  })`
136
446
  );
137
447
 
138
448
  const report = await runWarden({ rootDir: dir });
139
- const detourThrowRules = report.diagnostics.filter(
140
- (diagnostic) => diagnostic.rule === 'no-throw-in-detour-target'
449
+
450
+ const hasDraftFileMarkingWarn = report.diagnostics.some((diagnostic) =>
451
+ isDraftFileMarkingWarn(diagnostic)
141
452
  );
142
453
 
143
- expect(detourThrowRules).toHaveLength(1);
144
- expect(detourThrowRules[0]?.message).toContain('entity.search');
454
+ expect(hasDraftFileMarkingWarn).toBe(true);
145
455
  } finally {
146
456
  rmSync(dir, { force: true, recursive: true });
147
457
  }
@@ -192,7 +502,25 @@ describe('formatWardenReport', () => {
192
502
  passed: false,
193
503
  warnCount: 0,
194
504
  });
195
- expect(output).toContain('trailhead.lock is stale');
505
+ expect(output).toContain('trails.lock is stale');
196
506
  expect(output).toContain('Result: FAIL');
197
507
  });
508
+
509
+ test('formats a report with blocked established exports', () => {
510
+ const output = formatWardenReport({
511
+ diagnostics: [],
512
+ drift: {
513
+ blockedReason:
514
+ 'Established topo validation failed with 1 draft issue(s)',
515
+ committedHash: null,
516
+ currentHash: 'blocked',
517
+ stale: true,
518
+ },
519
+ errorCount: 0,
520
+ passed: false,
521
+ warnCount: 0,
522
+ });
523
+ expect(output).toContain('Drift: blocked');
524
+ expect(output).toContain('established exports blocked');
525
+ });
198
526
  });