@ontrails/warden 1.0.0-beta.3 → 1.0.0-beta.30

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 (260) hide show
  1. package/CHANGELOG.md +625 -9
  2. package/README.md +111 -26
  3. package/bin/warden.ts +50 -0
  4. package/package.json +28 -5
  5. package/src/adapter-check.ts +136 -0
  6. package/src/ast.ts +137 -0
  7. package/src/cli.ts +1415 -104
  8. package/src/command.ts +943 -0
  9. package/src/config.ts +184 -0
  10. package/src/draft.ts +22 -0
  11. package/src/drift.ts +122 -22
  12. package/src/fix.ts +120 -0
  13. package/src/formatters.ts +79 -9
  14. package/src/guide.ts +245 -0
  15. package/src/index.ts +215 -14
  16. package/src/project-context.ts +163 -0
  17. package/src/project-rules.ts +290 -0
  18. package/src/resolve.ts +531 -0
  19. package/src/rules/activation-orphan.ts +97 -0
  20. package/src/rules/ast.ts +4008 -86
  21. package/src/rules/circular-refs.ts +154 -0
  22. package/src/rules/cli-command-route-coherence.ts +177 -0
  23. package/src/rules/composes-declarations.ts +837 -0
  24. package/src/rules/context-no-surface-types.ts +78 -15
  25. package/src/rules/contour-exists.ts +251 -0
  26. package/src/rules/contour-ids.ts +15 -0
  27. package/src/rules/dead-internal-trail.ts +161 -0
  28. package/src/rules/dead-public-trail.ts +258 -0
  29. package/src/rules/draft-file-marking.ts +160 -0
  30. package/src/rules/draft-visible-debt.ts +87 -0
  31. package/src/rules/duplicate-public-contract.ts +91 -0
  32. package/src/rules/error-mapping-completeness.ts +290 -0
  33. package/src/rules/example-valid.ts +395 -0
  34. package/src/rules/fires-declarations.ts +735 -0
  35. package/src/rules/implementation-returns-result.ts +1411 -166
  36. package/src/rules/incomplete-accessor-for-standard-op.ts +272 -0
  37. package/src/rules/incomplete-crud.ts +581 -0
  38. package/src/rules/index.ts +234 -18
  39. package/src/rules/intent-propagation.ts +127 -0
  40. package/src/rules/layer-field-name-drift.ts +102 -0
  41. package/src/rules/library-projection-coherence.ts +100 -0
  42. package/src/rules/metadata.ts +755 -0
  43. package/src/rules/missing-reconcile.ts +98 -0
  44. package/src/rules/missing-visibility.ts +110 -0
  45. package/src/rules/no-destructured-compose.ts +194 -0
  46. package/src/rules/no-dev-permit-in-source.ts +99 -0
  47. package/src/rules/no-direct-implementation-call.ts +7 -7
  48. package/src/rules/no-legacy-layer-imports.ts +211 -0
  49. package/src/rules/no-native-error-result.ts +118 -0
  50. package/src/rules/no-redundant-result-error-wrap.ts +384 -0
  51. package/src/rules/no-retired-cross-vocabulary.ts +194 -0
  52. package/src/rules/no-sync-result-assumption.ts +1135 -98
  53. package/src/rules/no-throw-in-detour-recover.ts +225 -0
  54. package/src/rules/no-throw-in-implementation.ts +10 -9
  55. package/src/rules/no-top-level-surface.ts +371 -0
  56. package/src/rules/on-references-exist.ts +194 -0
  57. package/src/rules/orphaned-signal.ts +150 -0
  58. package/src/rules/owner-projection-parity.ts +143 -0
  59. package/src/rules/permit-governance.ts +25 -0
  60. package/src/rules/public-export-example-coverage.ts +562 -0
  61. package/src/rules/public-internal-deep-imports.ts +517 -0
  62. package/src/rules/public-output-schema.ts +29 -0
  63. package/src/rules/public-union-output-discriminants.ts +150 -0
  64. package/src/rules/read-intent-fires.ts +187 -0
  65. package/src/rules/reference-exists.ts +98 -0
  66. package/src/rules/registry-names.ts +155 -0
  67. package/src/rules/resolved-import-boundary.ts +146 -0
  68. package/src/rules/resource-declarations.ts +693 -0
  69. package/src/rules/resource-exists.ts +179 -0
  70. package/src/rules/resource-id-grammar.ts +65 -0
  71. package/src/rules/resource-mock-coverage.ts +115 -0
  72. package/src/rules/scan.ts +38 -25
  73. package/src/rules/scheduled-destroy-intent.ts +44 -0
  74. package/src/rules/signal-graph-coaching.ts +191 -0
  75. package/src/rules/specs.ts +9 -5
  76. package/src/rules/static-resource-accessor-preference.ts +649 -0
  77. package/src/rules/surface-facet-coherence.ts +362 -0
  78. package/src/rules/trail-fork-coaching.ts +616 -0
  79. package/src/rules/trail-versioning-source.ts +1072 -0
  80. package/src/rules/trail-versioning-topo.ts +172 -0
  81. package/src/rules/types.ts +272 -6
  82. package/src/rules/unmaterialized-activation-source.ts +84 -0
  83. package/src/rules/unreachable-detour-shadowing.ts +339 -0
  84. package/src/rules/valid-describe-refs.ts +162 -32
  85. package/src/rules/valid-detour-contract.ts +78 -0
  86. package/src/rules/warden-export-symmetry.ts +540 -0
  87. package/src/rules/warden-rules-use-ast.ts +1114 -0
  88. package/src/rules/webhook-route-collision.ts +243 -0
  89. package/src/trails/activation-orphan.trail.ts +84 -0
  90. package/src/trails/circular-refs.trail.ts +29 -0
  91. package/src/trails/cli-command-route-coherence.trail.ts +47 -0
  92. package/src/trails/composes-declarations.trail.ts +22 -0
  93. package/src/trails/context-no-surface-types.trail.ts +21 -0
  94. package/src/trails/contour-exists.trail.ts +21 -0
  95. package/src/trails/dead-internal-trail.trail.ts +26 -0
  96. package/src/trails/dead-public-trail.trail.ts +31 -0
  97. package/src/trails/deprecation-without-guidance.trail.ts +21 -0
  98. package/src/trails/draft-file-marking.trail.ts +16 -0
  99. package/src/trails/draft-visible-debt.trail.ts +16 -0
  100. package/src/trails/duplicate-public-contract.trail.ts +47 -0
  101. package/src/trails/error-mapping-completeness.trail.ts +30 -0
  102. package/src/trails/example-valid.trail.ts +25 -0
  103. package/src/trails/fires-declarations.trail.ts +23 -0
  104. package/src/trails/fork-without-preserved-blaze.trail.ts +31 -0
  105. package/src/trails/implementation-returns-result.trail.ts +20 -0
  106. package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
  107. package/src/trails/incomplete-crud.trail.ts +39 -0
  108. package/src/trails/index.ts +83 -0
  109. package/src/trails/intent-propagation.trail.ts +30 -0
  110. package/src/trails/layer-field-name-drift.trail.ts +39 -0
  111. package/src/trails/library-projection-coherence.trail.ts +43 -0
  112. package/src/trails/marker-schema-unsupported.trail.ts +23 -0
  113. package/src/trails/missing-reconcile.trail.ts +33 -0
  114. package/src/trails/missing-visibility.trail.ts +22 -0
  115. package/src/trails/no-destructured-compose.trail.ts +44 -0
  116. package/src/trails/no-dev-permit-in-source.trail.ts +16 -0
  117. package/src/trails/no-direct-implementation-call.trail.ts +16 -0
  118. package/src/trails/no-legacy-layer-imports.trail.ts +41 -0
  119. package/src/trails/no-native-error-result.trail.ts +18 -0
  120. package/src/trails/no-redundant-result-error-wrap.trail.ts +55 -0
  121. package/src/trails/no-retired-cross-vocabulary.trail.ts +42 -0
  122. package/src/trails/no-sync-result-assumption.trail.ts +19 -0
  123. package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
  124. package/src/trails/no-throw-in-implementation.trail.ts +20 -0
  125. package/src/trails/no-top-level-surface.trail.ts +43 -0
  126. package/src/trails/on-references-exist.trail.ts +21 -0
  127. package/src/trails/orphaned-signal.trail.ts +36 -0
  128. package/src/trails/owner-projection-parity.trail.ts +26 -0
  129. package/src/trails/pending-force.trail.ts +21 -0
  130. package/src/trails/permit-governance.trail.ts +51 -0
  131. package/src/trails/prefer-schema-inference.trail.ts +21 -0
  132. package/src/trails/public-export-example-coverage.trail.ts +16 -0
  133. package/src/trails/public-internal-deep-imports.trail.ts +94 -0
  134. package/src/trails/public-output-schema.trail.ts +55 -0
  135. package/src/trails/public-union-output-discriminants.trail.ts +33 -0
  136. package/src/trails/read-intent-fires.trail.ts +20 -0
  137. package/src/trails/reference-exists.trail.ts +25 -0
  138. package/src/trails/resolved-import-boundary.trail.ts +109 -0
  139. package/src/trails/resource-declarations.trail.ts +25 -0
  140. package/src/trails/resource-exists.trail.ts +27 -0
  141. package/src/trails/resource-id-grammar.trail.ts +39 -0
  142. package/src/trails/resource-mock-coverage.trail.ts +40 -0
  143. package/src/trails/run.ts +162 -0
  144. package/src/trails/scheduled-destroy-intent.trail.ts +56 -0
  145. package/src/trails/schema.ts +198 -0
  146. package/src/trails/signal-graph-coaching.trail.ts +77 -0
  147. package/src/trails/static-resource-accessor-preference.trail.ts +25 -0
  148. package/src/trails/surface-facet-coherence.trail.ts +25 -0
  149. package/src/trails/topo.ts +6 -0
  150. package/src/trails/trail-fork-coaching.trail.ts +42 -0
  151. package/src/trails/unmaterialized-activation-source.trail.ts +72 -0
  152. package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
  153. package/src/trails/valid-describe-refs.trail.ts +18 -0
  154. package/src/trails/valid-detour-contract.trail.ts +71 -0
  155. package/src/trails/version-gap.trail.ts +35 -0
  156. package/src/trails/version-pinned-compose.trail.ts +23 -0
  157. package/src/trails/version-without-examples.trail.ts +38 -0
  158. package/src/trails/warden-export-symmetry.trail.ts +16 -0
  159. package/src/trails/warden-rules-use-ast.trail.ts +64 -0
  160. package/src/trails/webhook-route-collision.trail.ts +50 -0
  161. package/src/trails/wrap-rule.ts +214 -0
  162. package/src/workspaces.ts +238 -0
  163. package/.turbo/turbo-build.log +0 -1
  164. package/.turbo/turbo-lint.log +0 -3
  165. package/.turbo/turbo-typecheck.log +0 -1
  166. package/dist/cli.d.ts +0 -46
  167. package/dist/cli.d.ts.map +0 -1
  168. package/dist/cli.js +0 -221
  169. package/dist/cli.js.map +0 -1
  170. package/dist/drift.d.ts +0 -26
  171. package/dist/drift.d.ts.map +0 -1
  172. package/dist/drift.js +0 -27
  173. package/dist/drift.js.map +0 -1
  174. package/dist/formatters.d.ts +0 -29
  175. package/dist/formatters.d.ts.map +0 -1
  176. package/dist/formatters.js +0 -87
  177. package/dist/formatters.js.map +0 -1
  178. package/dist/index.d.ts +0 -26
  179. package/dist/index.d.ts.map +0 -1
  180. package/dist/index.js +0 -26
  181. package/dist/index.js.map +0 -1
  182. package/dist/rules/ast.d.ts +0 -41
  183. package/dist/rules/ast.d.ts.map +0 -1
  184. package/dist/rules/ast.js +0 -163
  185. package/dist/rules/ast.js.map +0 -1
  186. package/dist/rules/context-no-surface-types.d.ts +0 -12
  187. package/dist/rules/context-no-surface-types.d.ts.map +0 -1
  188. package/dist/rules/context-no-surface-types.js +0 -96
  189. package/dist/rules/context-no-surface-types.js.map +0 -1
  190. package/dist/rules/implementation-returns-result.d.ts +0 -13
  191. package/dist/rules/implementation-returns-result.d.ts.map +0 -1
  192. package/dist/rules/implementation-returns-result.js +0 -277
  193. package/dist/rules/implementation-returns-result.js.map +0 -1
  194. package/dist/rules/index.d.ts +0 -22
  195. package/dist/rules/index.d.ts.map +0 -1
  196. package/dist/rules/index.js +0 -41
  197. package/dist/rules/index.js.map +0 -1
  198. package/dist/rules/no-direct-impl-in-route.d.ts +0 -12
  199. package/dist/rules/no-direct-impl-in-route.d.ts.map +0 -1
  200. package/dist/rules/no-direct-impl-in-route.js +0 -46
  201. package/dist/rules/no-direct-impl-in-route.js.map +0 -1
  202. package/dist/rules/no-direct-implementation-call.d.ts +0 -12
  203. package/dist/rules/no-direct-implementation-call.d.ts.map +0 -1
  204. package/dist/rules/no-direct-implementation-call.js +0 -39
  205. package/dist/rules/no-direct-implementation-call.js.map +0 -1
  206. package/dist/rules/no-sync-result-assumption.d.ts +0 -6
  207. package/dist/rules/no-sync-result-assumption.d.ts.map +0 -1
  208. package/dist/rules/no-sync-result-assumption.js +0 -98
  209. package/dist/rules/no-sync-result-assumption.js.map +0 -1
  210. package/dist/rules/no-throw-in-detour-target.d.ts +0 -12
  211. package/dist/rules/no-throw-in-detour-target.d.ts.map +0 -1
  212. package/dist/rules/no-throw-in-detour-target.js +0 -87
  213. package/dist/rules/no-throw-in-detour-target.js.map +0 -1
  214. package/dist/rules/no-throw-in-implementation.d.ts +0 -9
  215. package/dist/rules/no-throw-in-implementation.d.ts.map +0 -1
  216. package/dist/rules/no-throw-in-implementation.js +0 -34
  217. package/dist/rules/no-throw-in-implementation.js.map +0 -1
  218. package/dist/rules/prefer-schema-inference.d.ts +0 -7
  219. package/dist/rules/prefer-schema-inference.d.ts.map +0 -1
  220. package/dist/rules/prefer-schema-inference.js +0 -86
  221. package/dist/rules/prefer-schema-inference.js.map +0 -1
  222. package/dist/rules/scan.d.ts +0 -8
  223. package/dist/rules/scan.d.ts.map +0 -1
  224. package/dist/rules/scan.js +0 -32
  225. package/dist/rules/scan.js.map +0 -1
  226. package/dist/rules/specs.d.ts +0 -29
  227. package/dist/rules/specs.d.ts.map +0 -1
  228. package/dist/rules/specs.js +0 -192
  229. package/dist/rules/specs.js.map +0 -1
  230. package/dist/rules/structure.d.ts +0 -13
  231. package/dist/rules/structure.d.ts.map +0 -1
  232. package/dist/rules/structure.js +0 -142
  233. package/dist/rules/structure.js.map +0 -1
  234. package/dist/rules/types.d.ts +0 -52
  235. package/dist/rules/types.d.ts.map +0 -1
  236. package/dist/rules/types.js +0 -2
  237. package/dist/rules/types.js.map +0 -1
  238. package/dist/rules/valid-describe-refs.d.ts +0 -7
  239. package/dist/rules/valid-describe-refs.d.ts.map +0 -1
  240. package/dist/rules/valid-describe-refs.js +0 -51
  241. package/dist/rules/valid-describe-refs.js.map +0 -1
  242. package/dist/rules/valid-detour-refs.d.ts +0 -6
  243. package/dist/rules/valid-detour-refs.d.ts.map +0 -1
  244. package/dist/rules/valid-detour-refs.js +0 -116
  245. package/dist/rules/valid-detour-refs.js.map +0 -1
  246. package/src/__tests__/cli.test.ts +0 -198
  247. package/src/__tests__/drift.test.ts +0 -74
  248. package/src/__tests__/formatters.test.ts +0 -157
  249. package/src/__tests__/implementation-returns-result.test.ts +0 -128
  250. package/src/__tests__/no-direct-implementation-call.test.ts +0 -83
  251. package/src/__tests__/no-sync-result-assumption.test.ts +0 -85
  252. package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
  253. package/src/__tests__/prefer-schema-inference.test.ts +0 -84
  254. package/src/__tests__/rules.test.ts +0 -215
  255. package/src/__tests__/valid-describe-refs.test.ts +0 -60
  256. package/src/rules/no-direct-impl-in-route.ts +0 -77
  257. package/src/rules/no-throw-in-detour-target.ts +0 -150
  258. package/src/rules/valid-detour-refs.ts +0 -187
  259. package/tsconfig.json +0 -9
  260. package/tsconfig.tsbuildinfo +0 -1
@@ -1,54 +1,270 @@
1
+ import { activationOrphan } from './activation-orphan.js';
2
+ import { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
3
+ import { circularRefs } from './circular-refs.js';
4
+ import { contourExists } from './contour-exists.js';
1
5
  import { contextNoSurfaceTypes } from './context-no-surface-types.js';
6
+ import { composesDeclarations } from './composes-declarations.js';
7
+ import { deadInternalTrail } from './dead-internal-trail.js';
8
+ import { deadPublicTrail } from './dead-public-trail.js';
9
+ import { draftFileMarking } from './draft-file-marking.js';
10
+ import { draftVisibleDebt } from './draft-visible-debt.js';
11
+ import { duplicatePublicContract } from './duplicate-public-contract.js';
12
+ import { errorMappingCompleteness } from './error-mapping-completeness.js';
13
+ import { exampleValid } from './example-valid.js';
14
+ import { firesDeclarations } from './fires-declarations.js';
2
15
  import { implementationReturnsResult } from './implementation-returns-result.js';
3
- import { noDirectImplInRoute } from './no-direct-impl-in-route.js';
16
+ import { incompleteAccessorForStandardOp } from './incomplete-accessor-for-standard-op.js';
17
+ import { incompleteCrud } from './incomplete-crud.js';
18
+ import { intentPropagation } from './intent-propagation.js';
19
+ import { layerFieldNameDrift } from './layer-field-name-drift.js';
20
+ import { libraryProjectionCoherence } from './library-projection-coherence.js';
21
+ import { missingVisibility } from './missing-visibility.js';
22
+ import { missingReconcile } from './missing-reconcile.js';
23
+ import { noDevPermitInSource } from './no-dev-permit-in-source.js';
24
+ import { noDestructuredCompose } from './no-destructured-compose.js';
25
+ import { noLegacyLayerImports } from './no-legacy-layer-imports.js';
4
26
  import { noDirectImplementationCall } from './no-direct-implementation-call.js';
27
+ import { noNativeErrorResult } from './no-native-error-result.js';
28
+ import { noRedundantResultErrorWrap } from './no-redundant-result-error-wrap.js';
29
+ import { noRetiredCrossVocabulary } from './no-retired-cross-vocabulary.js';
5
30
  import { noSyncResultAssumption } from './no-sync-result-assumption.js';
6
- import { noThrowInDetourTarget } from './no-throw-in-detour-target.js';
31
+ import { noThrowInDetourRecover } from './no-throw-in-detour-recover.js';
7
32
  import { noThrowInImplementation } from './no-throw-in-implementation.js';
33
+ import { noTopLevelSurface } from './no-top-level-surface.js';
34
+ import { onReferencesExist } from './on-references-exist.js';
35
+ import { orphanedSignal } from './orphaned-signal.js';
36
+ import { ownerProjectionParity } from './owner-projection-parity.js';
37
+ import { permitGovernance } from './permit-governance.js';
8
38
  import { preferSchemaInference } from './prefer-schema-inference.js';
9
- import type { WardenRule } from './types.js';
39
+ import { publicExportExampleCoverage } from './public-export-example-coverage.js';
40
+ import { publicInternalDeepImports } from './public-internal-deep-imports.js';
41
+ import { publicOutputSchema } from './public-output-schema.js';
42
+ import { publicUnionOutputDiscriminants } from './public-union-output-discriminants.js';
43
+ import { readIntentFires } from './read-intent-fires.js';
44
+ import { referenceExists } from './reference-exists.js';
45
+ import { resolvedImportBoundary } from './resolved-import-boundary.js';
46
+ import { resourceDeclarations } from './resource-declarations.js';
47
+ import { resourceExists } from './resource-exists.js';
48
+ import { resourceIdGrammar } from './resource-id-grammar.js';
49
+ import { resourceMockCoverage } from './resource-mock-coverage.js';
50
+ import { scheduledDestroyIntent } from './scheduled-destroy-intent.js';
51
+ import { signalGraphCoaching } from './signal-graph-coaching.js';
52
+ import { staticResourceAccessorPreference } from './static-resource-accessor-preference.js';
53
+ import { surfaceFacetCoherence } from './surface-facet-coherence.js';
54
+ import { trailForkCoaching } from './trail-fork-coaching.js';
55
+ import {
56
+ forkWithoutPreservedBlaze,
57
+ markerSchemaUnsupported,
58
+ versionPinnedCompose,
59
+ } from './trail-versioning-source.js';
60
+ import {
61
+ deprecationWithoutGuidance,
62
+ pendingForce,
63
+ versionGap,
64
+ versionWithoutExamples,
65
+ } from './trail-versioning-topo.js';
66
+ import type { TopoAwareWardenRule, WardenRule } from './types.js';
67
+ import { unmaterializedActivationSource } from './unmaterialized-activation-source.js';
68
+ import { unreachableDetourShadowing } from './unreachable-detour-shadowing.js';
69
+ import { validDetourContract } from './valid-detour-contract.js';
10
70
  import { validDescribeRefs } from './valid-describe-refs.js';
11
- import { validDetourRefs } from './valid-detour-refs.js';
71
+ import { wardenExportSymmetry } from './warden-export-symmetry.js';
72
+ import { wardenRulesUseAst } from './warden-rules-use-ast.js';
73
+ import { webhookRouteCollision } from './webhook-route-collision.js';
12
74
 
13
75
  export type {
76
+ WardenFix,
77
+ WardenFixCapability,
78
+ WardenFixClass,
79
+ WardenFixEdit,
80
+ WardenFixSafety,
81
+ WardenGuidance,
82
+ WardenGuidanceLink,
83
+ WardenRuleLifecycle,
84
+ WardenRuleLifecycleState,
85
+ WardenRuleConcern,
86
+ WardenRuleMetadata,
87
+ WardenRuleScope,
14
88
  ProjectAwareWardenRule,
15
89
  ProjectContext,
90
+ TopoAwareWardenRule,
16
91
  WardenDiagnostic,
17
92
  WardenRule,
93
+ WardenRuleTier,
18
94
  WardenSeverity,
19
95
  } from './types.js';
20
96
 
97
+ export {
98
+ builtinWardenRuleMetadata,
99
+ getWardenRuleMetadata,
100
+ listWardenRuleMetadata,
101
+ wardenFixClasses,
102
+ wardenFixSafeties,
103
+ wardenRuleConcerns,
104
+ wardenRuleLifecycleStates,
105
+ wardenRuleScopes,
106
+ wardenRuleTiers,
107
+ } from './metadata.js';
108
+ export type { BuiltinWardenRuleName } from './metadata.js';
109
+
110
+ export { activationOrphan } from './activation-orphan.js';
111
+ export { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
21
112
  export { noThrowInImplementation } from './no-throw-in-implementation.js';
113
+ export { circularRefs } from './circular-refs.js';
114
+ export { contourExists } from './contour-exists.js';
22
115
  export { contextNoSurfaceTypes } from './context-no-surface-types.js';
23
- export { validDetourRefs } from './valid-detour-refs.js';
24
- export { noDirectImplInRoute } from './no-direct-impl-in-route.js';
116
+ export { composesDeclarations } from './composes-declarations.js';
117
+ export { deadInternalTrail } from './dead-internal-trail.js';
118
+ export { deadPublicTrail } from './dead-public-trail.js';
119
+ export { draftFileMarking } from './draft-file-marking.js';
120
+ export { draftVisibleDebt } from './draft-visible-debt.js';
121
+ export { duplicatePublicContract } from './duplicate-public-contract.js';
122
+ export { errorMappingCompleteness } from './error-mapping-completeness.js';
123
+ export { exampleValid } from './example-valid.js';
124
+ export { firesDeclarations } from './fires-declarations.js';
125
+ export { incompleteAccessorForStandardOp } from './incomplete-accessor-for-standard-op.js';
126
+ export { incompleteCrud } from './incomplete-crud.js';
127
+ export { intentPropagation } from './intent-propagation.js';
128
+ export { libraryProjectionCoherence } from './library-projection-coherence.js';
129
+ export { layerFieldNameDrift } from './layer-field-name-drift.js';
130
+ export { missingVisibility } from './missing-visibility.js';
131
+ export { missingReconcile } from './missing-reconcile.js';
132
+ export { onReferencesExist } from './on-references-exist.js';
133
+ export { noDevPermitInSource } from './no-dev-permit-in-source.js';
134
+ export { noDestructuredCompose } from './no-destructured-compose.js';
135
+ export { noLegacyLayerImports } from './no-legacy-layer-imports.js';
25
136
  export { noDirectImplementationCall } from './no-direct-implementation-call.js';
137
+ export { noNativeErrorResult } from './no-native-error-result.js';
138
+ export { noRedundantResultErrorWrap } from './no-redundant-result-error-wrap.js';
139
+ export { noRetiredCrossVocabulary } from './no-retired-cross-vocabulary.js';
26
140
  export { noSyncResultAssumption } from './no-sync-result-assumption.js';
27
141
  export { implementationReturnsResult } from './implementation-returns-result.js';
28
- export { noThrowInDetourTarget } from './no-throw-in-detour-target.js';
142
+ export { noThrowInDetourRecover } from './no-throw-in-detour-recover.js';
143
+ export { noTopLevelSurface } from './no-top-level-surface.js';
144
+ export { orphanedSignal } from './orphaned-signal.js';
145
+ export { ownerProjectionParity } from './owner-projection-parity.js';
146
+ export { permitGovernance } from './permit-governance.js';
29
147
  export { preferSchemaInference } from './prefer-schema-inference.js';
148
+ export { publicInternalDeepImports } from './public-internal-deep-imports.js';
149
+ export { publicOutputSchema } from './public-output-schema.js';
150
+ export { publicUnionOutputDiscriminants } from './public-union-output-discriminants.js';
151
+ export { readIntentFires } from './read-intent-fires.js';
152
+ export { referenceExists } from './reference-exists.js';
153
+ export { resolvedImportBoundary } from './resolved-import-boundary.js';
154
+ export { resourceDeclarations } from './resource-declarations.js';
155
+ export { resourceExists } from './resource-exists.js';
156
+ export { resourceIdGrammar } from './resource-id-grammar.js';
157
+ export { resourceMockCoverage } from './resource-mock-coverage.js';
158
+ export { scheduledDestroyIntent } from './scheduled-destroy-intent.js';
159
+ export { signalGraphCoaching } from './signal-graph-coaching.js';
160
+ export { staticResourceAccessorPreference } from './static-resource-accessor-preference.js';
161
+ export { surfaceFacetCoherence } from './surface-facet-coherence.js';
162
+ export { trailForkCoaching } from './trail-fork-coaching.js';
163
+ export {
164
+ forkWithoutPreservedBlaze,
165
+ markerSchemaUnsupported,
166
+ versionPinnedCompose,
167
+ } from './trail-versioning-source.js';
168
+ export {
169
+ deprecationWithoutGuidance,
170
+ pendingForce,
171
+ versionGap,
172
+ versionWithoutExamples,
173
+ } from './trail-versioning-topo.js';
174
+ export { unmaterializedActivationSource } from './unmaterialized-activation-source.js';
175
+ export { unreachableDetourShadowing } from './unreachable-detour-shadowing.js';
176
+ export { validDetourContract } from './valid-detour-contract.js';
30
177
  export { validDescribeRefs } from './valid-describe-refs.js';
178
+ export { webhookRouteCollision } from './webhook-route-collision.js';
31
179
 
32
- /**
33
- * All built-in warden rules, keyed by rule name.
34
- *
35
- * Rules that duplicate validateTopo checks (follows-trails-exist,
36
- * no-recursive-follows, event-origins-exist, examples-match-schema,
37
- * require-output-schema) and follows-matches-calls (now covered by
38
- * testExamples follows coverage) have been removed.
39
- */
180
+ /** All built-in warden rules, keyed by rule name. */
40
181
  export const wardenRules: ReadonlyMap<string, WardenRule> = new Map<
41
182
  string,
42
183
  WardenRule
43
184
  >([
44
185
  [noThrowInImplementation.name, noThrowInImplementation],
186
+ [circularRefs.name, circularRefs],
187
+ [contourExists.name, contourExists],
45
188
  [contextNoSurfaceTypes.name, contextNoSurfaceTypes],
189
+ [composesDeclarations.name, composesDeclarations],
190
+ [deadInternalTrail.name, deadInternalTrail],
191
+ [deadPublicTrail.name, deadPublicTrail],
192
+ [draftFileMarking.name, draftFileMarking],
193
+ [draftVisibleDebt.name, draftVisibleDebt],
194
+ [errorMappingCompleteness.name, errorMappingCompleteness],
195
+ [exampleValid.name, exampleValid],
196
+ [firesDeclarations.name, firesDeclarations],
197
+ [incompleteCrud.name, incompleteCrud],
198
+ [intentPropagation.name, intentPropagation],
199
+ [layerFieldNameDrift.name, layerFieldNameDrift],
200
+ [missingVisibility.name, missingVisibility],
201
+ [missingReconcile.name, missingReconcile],
202
+ [onReferencesExist.name, onReferencesExist],
203
+ [orphanedSignal.name, orphanedSignal],
204
+ [ownerProjectionParity.name, ownerProjectionParity],
205
+ [publicExportExampleCoverage.name, publicExportExampleCoverage],
206
+ [publicInternalDeepImports.name, publicInternalDeepImports],
207
+ [resourceDeclarations.name, resourceDeclarations],
208
+ [readIntentFires.name, readIntentFires],
209
+ [referenceExists.name, referenceExists],
210
+ [resolvedImportBoundary.name, resolvedImportBoundary],
211
+ [resourceIdGrammar.name, resourceIdGrammar],
212
+ [resourceExists.name, resourceExists],
213
+ [resourceMockCoverage.name, resourceMockCoverage],
46
214
  [preferSchemaInference.name, preferSchemaInference],
215
+ [staticResourceAccessorPreference.name, staticResourceAccessorPreference],
216
+ [surfaceFacetCoherence.name, surfaceFacetCoherence],
217
+ [trailForkCoaching.name, trailForkCoaching],
47
218
  [validDescribeRefs.name, validDescribeRefs],
48
- [validDetourRefs.name, validDetourRefs],
219
+ [noDevPermitInSource.name, noDevPermitInSource],
220
+ [noDestructuredCompose.name, noDestructuredCompose],
49
221
  [noDirectImplementationCall.name, noDirectImplementationCall],
222
+ [noLegacyLayerImports.name, noLegacyLayerImports],
223
+ [noNativeErrorResult.name, noNativeErrorResult],
224
+ [noRedundantResultErrorWrap.name, noRedundantResultErrorWrap],
225
+ [noRetiredCrossVocabulary.name, noRetiredCrossVocabulary],
50
226
  [noSyncResultAssumption.name, noSyncResultAssumption],
51
227
  [implementationReturnsResult.name, implementationReturnsResult],
52
- [noThrowInDetourTarget.name, noThrowInDetourTarget],
53
- [noDirectImplInRoute.name, noDirectImplInRoute],
228
+ [noThrowInDetourRecover.name, noThrowInDetourRecover],
229
+ [noTopLevelSurface.name, noTopLevelSurface],
230
+ [unreachableDetourShadowing.name, unreachableDetourShadowing],
231
+ [wardenExportSymmetry.name, wardenExportSymmetry],
232
+ [wardenRulesUseAst.name, wardenRulesUseAst],
233
+ [forkWithoutPreservedBlaze.name, forkWithoutPreservedBlaze],
234
+ [markerSchemaUnsupported.name, markerSchemaUnsupported],
235
+ [versionPinnedCompose.name, versionPinnedCompose],
54
236
  ]);
237
+
238
+ /**
239
+ * Built-in topo-aware warden rules, keyed by rule name.
240
+ *
241
+ * These rules inspect the compiled runtime trail graph once per topo,
242
+ * rather than scanning source files. Kept in a separate registry because
243
+ * their dispatch shape differs from `WardenRule` / `ProjectAwareWardenRule`.
244
+ *
245
+ * @remarks
246
+ * Topo-aware rules only fire when the warden runtime is invoked with a
247
+ * resolved `Topo` (see `WardenOptions.topo`). Runs without a topo — e.g.
248
+ * pure source-directory lints — silently skip this registry. Rules
249
+ * registered here must tolerate non-execution when no topo is available.
250
+ */
251
+ export const wardenTopoRules: ReadonlyMap<string, TopoAwareWardenRule> =
252
+ new Map<string, TopoAwareWardenRule>([
253
+ [activationOrphan.name, activationOrphan],
254
+ [cliCommandRouteCoherence.name, cliCommandRouteCoherence],
255
+ [duplicatePublicContract.name, duplicatePublicContract],
256
+ [incompleteAccessorForStandardOp.name, incompleteAccessorForStandardOp],
257
+ [libraryProjectionCoherence.name, libraryProjectionCoherence],
258
+ [permitGovernance.name, permitGovernance],
259
+ [publicOutputSchema.name, publicOutputSchema],
260
+ [publicUnionOutputDiscriminants.name, publicUnionOutputDiscriminants],
261
+ [scheduledDestroyIntent.name, scheduledDestroyIntent],
262
+ [signalGraphCoaching.name, signalGraphCoaching],
263
+ [unmaterializedActivationSource.name, unmaterializedActivationSource],
264
+ [validDetourContract.name, validDetourContract],
265
+ [deprecationWithoutGuidance.name, deprecationWithoutGuidance],
266
+ [pendingForce.name, pendingForce],
267
+ [versionGap.name, versionGap],
268
+ [versionWithoutExamples.name, versionWithoutExamples],
269
+ [webhookRouteCollision.name, webhookRouteCollision],
270
+ ]);
@@ -0,0 +1,127 @@
1
+ import type { Intent } from '@ontrails/core';
2
+ import {
3
+ collectNamedTrailIds,
4
+ collectTrailIntentsById,
5
+ extractDefinitionComposeTargetIds,
6
+ findTrailDefinitions,
7
+ offsetToLine,
8
+ parse,
9
+ } from './ast.js';
10
+ import type { AstNode } from './ast.js';
11
+ import { isTestFile } from './scan.js';
12
+ import type {
13
+ ProjectAwareWardenRule,
14
+ ProjectContext,
15
+ WardenDiagnostic,
16
+ } from './types.js';
17
+
18
+ const buildIntentPropagationDiagnostic = (
19
+ trailId: string,
20
+ targetTrailId: string,
21
+ targetIntent: Exclude<Intent, 'read'>,
22
+ filePath: string,
23
+ line: number
24
+ ): WardenDiagnostic => ({
25
+ filePath,
26
+ line,
27
+ message: `Trail "${trailId}" declares intent: 'read' but composes "${targetTrailId}" with intent: '${targetIntent}'. Read trails must not compose write or destroy side effects.`,
28
+ rule: 'intent-propagation',
29
+ severity: 'warn',
30
+ });
31
+
32
+ const buildDiagnosticsForComposeTargets = (
33
+ trailId: string,
34
+ targetTrailIds: readonly string[],
35
+ filePath: string,
36
+ line: number,
37
+ trailIntentsById: ReadonlyMap<string, Intent>
38
+ ): readonly WardenDiagnostic[] =>
39
+ targetTrailIds.flatMap((targetTrailId) => {
40
+ const targetIntent = trailIntentsById.get(targetTrailId);
41
+ if (!targetIntent || targetIntent === 'read') {
42
+ return [];
43
+ }
44
+
45
+ return [
46
+ buildIntentPropagationDiagnostic(
47
+ trailId,
48
+ targetTrailId,
49
+ targetIntent,
50
+ filePath,
51
+ line
52
+ ),
53
+ ];
54
+ });
55
+
56
+ const buildDiagnosticsForTrail = (
57
+ def: ReturnType<typeof findTrailDefinitions>[number],
58
+ sourceCode: string,
59
+ filePath: string,
60
+ namedTrailIds: ReadonlyMap<string, string>,
61
+ trailIntentsById: ReadonlyMap<string, Intent>
62
+ ): readonly WardenDiagnostic[] => {
63
+ if (def.kind !== 'trail' || trailIntentsById.get(def.id) !== 'read') {
64
+ return [];
65
+ }
66
+
67
+ return buildDiagnosticsForComposeTargets(
68
+ def.id,
69
+ extractDefinitionComposeTargetIds(def.config, sourceCode, namedTrailIds),
70
+ filePath,
71
+ offsetToLine(sourceCode, def.start),
72
+ trailIntentsById
73
+ );
74
+ };
75
+
76
+ const checkIntentPropagation = (
77
+ ast: AstNode | null,
78
+ sourceCode: string,
79
+ filePath: string,
80
+ trailIntentsById: ReadonlyMap<string, Intent>
81
+ ): readonly WardenDiagnostic[] => {
82
+ if (isTestFile(filePath) || !ast) {
83
+ return [];
84
+ }
85
+
86
+ const namedTrailIds = collectNamedTrailIds(ast);
87
+ return findTrailDefinitions(ast).flatMap((def) =>
88
+ buildDiagnosticsForTrail(
89
+ def,
90
+ sourceCode,
91
+ filePath,
92
+ namedTrailIds,
93
+ trailIntentsById
94
+ )
95
+ );
96
+ };
97
+
98
+ export const intentPropagation: ProjectAwareWardenRule = {
99
+ check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
100
+ const ast = parse(filePath, sourceCode);
101
+ return checkIntentPropagation(
102
+ ast,
103
+ sourceCode,
104
+ filePath,
105
+ ast ? collectTrailIntentsById(ast) : new Map<string, Intent>()
106
+ );
107
+ },
108
+ checkWithContext(
109
+ sourceCode: string,
110
+ filePath: string,
111
+ context: ProjectContext
112
+ ): readonly WardenDiagnostic[] {
113
+ const ast = parse(filePath, sourceCode);
114
+ const localTrailIntentsById = ast
115
+ ? collectTrailIntentsById(ast)
116
+ : new Map<string, Intent>();
117
+ const trailIntentsById = new Map(localTrailIntentsById);
118
+ for (const [trailId, intent] of context.trailIntentsById ?? []) {
119
+ trailIntentsById.set(trailId, intent);
120
+ }
121
+ return checkIntentPropagation(ast, sourceCode, filePath, trailIntentsById);
122
+ },
123
+ description:
124
+ "Warn when a trail declaring intent: 'read' composes a trail whose normalized intent is write or destroy.",
125
+ name: 'intent-propagation',
126
+ severity: 'warn',
127
+ };
@@ -0,0 +1,102 @@
1
+ import { sep } from 'node:path';
2
+
3
+ import {
4
+ getNodeCallee,
5
+ getNodeId,
6
+ getNodeInit,
7
+ identifierName,
8
+ offsetToLine,
9
+ parse,
10
+ walk,
11
+ } from './ast.js';
12
+ import type { AstNode } from './ast.js';
13
+ import type { WardenDiagnostic, WardenRule } from './types.js';
14
+
15
+ const RULE_NAME = 'layer-field-name-drift';
16
+
17
+ const LEGACY_RESERVED_SET_NAMES = new Set(['META_FLAG_CANDIDATES']);
18
+
19
+ const normalizePath = (filePath: string): string =>
20
+ filePath.split(sep).join('/');
21
+
22
+ const isSurfaceSourceFile = (filePath: string): boolean => {
23
+ const normalized = normalizePath(filePath);
24
+ return /(^|\/)packages\/(cli|http|mcp)\/src\//.test(normalized);
25
+ };
26
+
27
+ const isTestFile = (filePath: string): boolean => {
28
+ const normalized = normalizePath(filePath);
29
+ return (
30
+ normalized.includes('/__tests__/') || /\.test\.[cm]?tsx?$/.test(normalized)
31
+ );
32
+ };
33
+
34
+ const looksLikeLayerReservedNameSet = (name: string): boolean => {
35
+ const lower = name.toLowerCase();
36
+ return (
37
+ LEGACY_RESERVED_SET_NAMES.has(name) ||
38
+ (lower.includes('layer') &&
39
+ lower.includes('reserved') &&
40
+ (lower.includes('name') || lower.includes('flag')))
41
+ );
42
+ };
43
+
44
+ const isSetConstruction = (node: AstNode | undefined): boolean => {
45
+ if (node?.type !== 'NewExpression') {
46
+ return false;
47
+ }
48
+ const callee = getNodeCallee(node);
49
+ return identifierName(callee) === 'Set';
50
+ };
51
+
52
+ const isLocalReservedSetInitializer = (node: AstNode | undefined): boolean =>
53
+ node?.type === 'ArrayExpression' || isSetConstruction(node);
54
+
55
+ const buildDiagnostic = (
56
+ sourceCode: string,
57
+ filePath: string,
58
+ node: AstNode,
59
+ name: string
60
+ ): WardenDiagnostic => ({
61
+ filePath,
62
+ line: offsetToLine(sourceCode, node.start),
63
+ message: `layer-field-name-drift: surface-local reserved name set "${name}" can make layer input fields project differently across surfaces. Import LAYER_FIELD_RESERVED_NAMES from @ontrails/core instead.`,
64
+ rule: RULE_NAME,
65
+ severity: 'error',
66
+ });
67
+
68
+ export const layerFieldNameDrift: WardenRule = {
69
+ check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
70
+ if (!isSurfaceSourceFile(filePath) || isTestFile(filePath)) {
71
+ return [];
72
+ }
73
+
74
+ const ast = parse(filePath, sourceCode);
75
+ if (!ast) {
76
+ return [];
77
+ }
78
+
79
+ const diagnostics: WardenDiagnostic[] = [];
80
+ walk(ast, (node) => {
81
+ if (node.type !== 'VariableDeclarator') {
82
+ return;
83
+ }
84
+ const id = getNodeId(node);
85
+ const init = getNodeInit(node);
86
+ const name = identifierName(id);
87
+ if (
88
+ name &&
89
+ looksLikeLayerReservedNameSet(name) &&
90
+ isLocalReservedSetInitializer(init)
91
+ ) {
92
+ diagnostics.push(buildDiagnostic(sourceCode, filePath, node, name));
93
+ }
94
+ });
95
+
96
+ return diagnostics;
97
+ },
98
+ description:
99
+ 'Prevent surface-local reserved-name sets from drifting layer input field projection across surfaces.',
100
+ name: RULE_NAME,
101
+ severity: 'error',
102
+ };
@@ -0,0 +1,100 @@
1
+ import { deriveTopoGraph } from '@ontrails/topographer';
2
+ import type {
3
+ TopoGraph,
4
+ TopoGraphLibraryProjection,
5
+ } from '@ontrails/topographer';
6
+
7
+ import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
8
+
9
+ const RULE_NAME = 'library-projection-coherence';
10
+ const TOPO_FILE = '<topo>';
11
+
12
+ const resolveGraph = (
13
+ topo: Parameters<TopoAwareWardenRule['checkTopo']>[0],
14
+ graph: TopoGraph | undefined
15
+ ): TopoGraph => graph ?? deriveTopoGraph(topo);
16
+
17
+ const renderTrailIds = (trailIds: readonly string[]): string =>
18
+ trailIds
19
+ .toSorted((left, right) => left.localeCompare(right))
20
+ .map((trailId) => `"${trailId}"`)
21
+ .join(', ');
22
+
23
+ const collisionDiagnostic = (
24
+ exportName: string,
25
+ trailIds: readonly string[]
26
+ ): WardenDiagnostic => ({
27
+ filePath: TOPO_FILE,
28
+ line: 1,
29
+ message: `Library projection export collision on "${exportName}": trails ${renderTrailIds(trailIds)} derive the same package export. Rename one trail or add a library export override before materializing the generated package.`,
30
+ rule: RULE_NAME,
31
+ severity: 'error',
32
+ });
33
+
34
+ const missingTargetDiagnostic = (
35
+ exportName: string,
36
+ trailId: string
37
+ ): WardenDiagnostic => ({
38
+ filePath: TOPO_FILE,
39
+ line: 1,
40
+ message: `Library projection export "${exportName}" targets unknown trail "${trailId}". Resolved library exports must stay attached to existing trail contracts.`,
41
+ rule: RULE_NAME,
42
+ severity: 'error',
43
+ });
44
+
45
+ const duplicateExportDiagnostic = (
46
+ exportName: string,
47
+ trailIds: readonly string[]
48
+ ): WardenDiagnostic => ({
49
+ filePath: TOPO_FILE,
50
+ line: 1,
51
+ message: `Library projection contains duplicate export "${exportName}" for trails ${renderTrailIds(trailIds)}. The resolved projection should record the collision and keep only one emitted export.`,
52
+ rule: RULE_NAME,
53
+ severity: 'error',
54
+ });
55
+
56
+ const collectDuplicateExportDiagnostics = (
57
+ projection: TopoGraphLibraryProjection
58
+ ): readonly WardenDiagnostic[] => {
59
+ const trailIdsByExport = new Map<string, string[]>();
60
+ for (const entry of projection.exports) {
61
+ const trailIds = trailIdsByExport.get(entry.exportName) ?? [];
62
+ trailIds.push(entry.trailId);
63
+ trailIdsByExport.set(entry.exportName, trailIds);
64
+ }
65
+ return [...trailIdsByExport.entries()]
66
+ .filter(([, trailIds]) => trailIds.length > 1)
67
+ .map(([exportName, trailIds]) =>
68
+ duplicateExportDiagnostic(exportName, trailIds)
69
+ );
70
+ };
71
+
72
+ const collectMissingTargetDiagnostics = (
73
+ projection: TopoGraphLibraryProjection,
74
+ knownTrailIds: ReadonlySet<string>
75
+ ): readonly WardenDiagnostic[] =>
76
+ projection.exports
77
+ .filter((entry) => !knownTrailIds.has(entry.trailId))
78
+ .map((entry) => missingTargetDiagnostic(entry.exportName, entry.trailId));
79
+
80
+ export const libraryProjectionCoherence: TopoAwareWardenRule = {
81
+ checkTopo(topo, context) {
82
+ const graph = resolveGraph(topo, context?.graph);
83
+ if (!graph.library) {
84
+ return [];
85
+ }
86
+
87
+ const knownTrailIds = new Set(topo.trails.keys());
88
+ return [
89
+ ...graph.library.collisions.map((collision) =>
90
+ collisionDiagnostic(collision.exportName, collision.trailIds)
91
+ ),
92
+ ...collectDuplicateExportDiagnostics(graph.library),
93
+ ...collectMissingTargetDiagnostics(graph.library, knownTrailIds),
94
+ ];
95
+ },
96
+ description:
97
+ 'Ensure resolved library projection exports are collision-free and target existing trails.',
98
+ name: RULE_NAME,
99
+ severity: 'error',
100
+ };