Repository: apigee/registry
Pull request #680 (2022-08-08 22:02:38)

Add metadata to conformance report
Summary of changes:
* Updated the `conformance_report` proto to include `display_name` and `description` in the `ruleReport`.
* Update `compute conformance` command logic to add the following to the report:
   - DisplayName
   - Description
   - specName with revisionID included

PS: The generated rpc code includes multiple changes due to a change in the `protoc-gen-go` version. The only significant change in the rpc folder is the one in the `ConformanceReport` proto.

codecov[bot]: # [Codecov](https://codecov.io/gh/apigee/registry/pull/680?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) Report
> Merging [#680](https://codecov.io/gh/apigee/registry/pull/680?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (e97f2b8) into [main](https://codecov.io/gh/apigee/registry/commit/213371d55124f849f5efb6ae88057ff74853c04c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) (213371d) will **increase** coverage by `55.55%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head e97f2b8 differs from pull request most recent head 5009f15. Consider uploading reports for the commit 5009f15 to get more accurate results

```diff
@@            Coverage Diff            @@
##           main     #680       +/-   ##
=========================================
+ Coverage      0   55.55%   +55.55%     
=========================================
  Files         0       89       +89     
  Lines         0     7711     +7711     
=========================================
+ Hits          0     4284     +4284     
- Misses        0     2972     +2972     
- Partials      0      455      +455     
```


| [Impacted Files](https://codecov.io/gh/apigee/registry/pull/680?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) | Coverage Δ | |
|---|---|---|
| [cmd/registry/conformance/conformance-task.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NvbmZvcm1hbmNlL2NvbmZvcm1hbmNlLXRhc2suZ28=) | `46.24% <100.00%> (ø)` | |
| [server/registry/actions\_deployment\_revisions.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-c2VydmVyL3JlZ2lzdHJ5L2FjdGlvbnNfZGVwbG95bWVudF9yZXZpc2lvbnMuZ28=) | `36.58% <0.00%> (ø)` | |
| [cmd/registry/cmd/apply/apply.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NtZC9hcHBseS9hcHBseS5nbw==) | `72.41% <0.00%> (ø)` | |
| [cmd/registry/cmd/upload/artifact.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NtZC91cGxvYWQvYXJ0aWZhY3QuZ28=) | `38.53% <0.00%> (ø)` | |
| [server/registry/names/api.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-c2VydmVyL3JlZ2lzdHJ5L25hbWVzL2FwaS5nbw==) | `42.62% <0.00%> (ø)` | |
| [cmd/registry/cmd/export/sheet.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NtZC9leHBvcnQvc2hlZXQuZ28=) | `7.92% <0.00%> (ø)` | |
| [cmd/registry/cmd/compute/references.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NtZC9jb21wdXRlL3JlZmVyZW5jZXMuZ28=) | `0.00% <0.00%> (ø)` | |
| [server/registry/actions\_apis.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-c2VydmVyL3JlZ2lzdHJ5L2FjdGlvbnNfYXBpcy5nbw==) | `64.08% <0.00%> (ø)` | |
| [cmd/registry/cmd/config/configurations/describe.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-Y21kL3JlZ2lzdHJ5L2NtZC9jb25maWcvY29uZmlndXJhdGlvbnMvZGVzY3JpYmUuZ28=) | `75.00% <0.00%> (ø)` | |
| [log/apex.go](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee#diff-bG9nL2FwZXguZ28=) | `72.09% <0.00%> (ø)` | |
| ... and [80 more](https://codecov.io/gh/apigee/registry/pull/680/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee) | |

:mega: Codecov can now indicate which changes are the most critical in Pull Requests. [Learn more](https://about.codecov.io/product/feature/runtime-insights/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apigee)


Patch:
diff --git a/cmd/registry/conformance/conformance-task.go b/cmd/registry/conformance/conformance-task.go
index 69b75c5..748526a 100644
--- a/cmd/registry/conformance/conformance-task.go
+++ b/cmd/registry/conformance/conformance-task.go
@@ -226,11 +226,14 @@ func (task *ComputeConformanceTask) computeConformanceReport(
 			}
 
 			ruleReport := &rpc.RuleReport{
-				RuleId:     guidelineRule.GetId(),
-				Spec:       task.Spec.GetName(),
-				File:       filepath.Base(lintFile.GetFilePath()),
-				Suggestion: problem.Suggestion,
-				Location:   problem.Location,
+				RuleId:      guidelineRule.GetId(),
+				Spec:        fmt.Sprintf("%s@%s", task.Spec.GetName(), task.Spec.GetRevisionId()),
+				File:        filepath.Base(lintFile.GetFilePath()),
+				Suggestion:  problem.Suggestion,
+				Location:    problem.Location,
+				DisplayName: guidelineRule.GetDisplayName(),
+				Description: guidelineRule.GetDescription(),
+				DocUri:      guidelineRule.GetDocUri(),
 			}
 			// Add the rule report to the appropriate guideline report.
 			guidelineGroup := conformanceReport.GuidelineReportGroups[guideline.GetState()]
diff --git a/google/cloud/apigeeregistry/v1/style/conformance_report.proto b/google/cloud/apigeeregistry/v1/style/conformance_report.proto
index 7d45f4b..06f3fbe 100644
--- a/google/cloud/apigeeregistry/v1/style/conformance_report.proto
+++ b/google/cloud/apigeeregistry/v1/style/conformance_report.proto
@@ -74,6 +74,15 @@ message RuleReport {
 
   // The location of the problem in the spec file.
   LintLocation location = 5;
+
+  // Human-meaningful name of the breached rule.
+  string display_name = 6;
+
+  // A detailed description of the breached rule.
+  string description = 7;
+
+  // A link to additional documentation relating to the breached rule.
+  string doc_uri = 8;
 }
 
 // GuidelineReportGroup is an abstraction that maps state
diff --git a/rpc/admin_models.pb.go b/rpc/admin_models.pb.go
index 1a7740e..3bdb71b 100644
--- a/rpc/admin_models.pb.go
+++ b/rpc/admin_models.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/admin_models.proto
 
diff --git a/rpc/admin_service.pb.go b/rpc/admin_service.pb.go
index b4324b2..5b51d7b 100644
--- a/rpc/admin_service.pb.go
+++ b/rpc/admin_service.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/admin_service.proto
 
@@ -199,6 +199,7 @@ type ListProjectsRequest struct {
 	// Expression Language and can refer to all message fields.
 	Filter string `protobuf:"bytes,3,opt,name=filter,proto3" json:"filter,omitempty"`
 	// A comma-separated list of fields, e.g. "foo,bar"
+	// Fields can be sorted in descending order using the "desc" identifier, e.g. "foo desc,bar"
 	OrderBy string `protobuf:"bytes,4,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
 }
 
diff --git a/rpc/conformance_report.pb.go b/rpc/conformance_report.pb.go
index 9c6be7f..73279da 100644
--- a/rpc/conformance_report.pb.go
+++ b/rpc/conformance_report.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/style/conformance_report.proto
 
@@ -194,6 +194,12 @@ type RuleReport struct {
 	Suggestion string `protobuf:"bytes,4,opt,name=suggestion,proto3" json:"suggestion,omitempty"`
 	// The location of the problem in the spec file.
 	Location *LintLocation `protobuf:"bytes,5,opt,name=location,proto3" json:"location,omitempty"`
+	// Human-meaningful name of the breached rule.
+	DisplayName string `protobuf:"bytes,6,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"`
+	// A detailed description of the breached rule.
+	Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"`
+	// A link to additional documentation relating to the breached rule.
+	DocUri string `protobuf:"bytes,8,opt,name=doc_uri,json=docUri,proto3" json:"doc_uri,omitempty"`
 }
 
 func (x *RuleReport) Reset() {
@@ -263,6 +269,27 @@ func (x *RuleReport) GetLocation() *LintLocation {
 	return nil
 }
 
+func (x *RuleReport) GetDisplayName() string {
+	if x != nil {
+		return x.DisplayName
+	}
+	return ""
+}
+
+func (x *RuleReport) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+func (x *RuleReport) GetDocUri() string {
+	if x != nil {
+		return x.DocUri
+	}
+	return ""
+}
+
 // GuidelineReportGroup is an abstraction that maps state
 // (PROPOSED, ACTIVE, DEPRECATED, DISABLED) to a list of
 // guideline reports for guidelines of that state.
@@ -424,7 +451,7 @@ var file_google_cloud_apigeeregistry_v1_style_conformance_report_proto_rawDesc =
 	0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
 	0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72,
 	0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, 0x72, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f,
-	0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0xcc, 0x01, 0x0a, 0x0a, 0x52, 0x75, 0x6c,
+	0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0xaa, 0x02, 0x0a, 0x0a, 0x52, 0x75, 0x6c,
 	0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1c, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f,
 	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x06, 0x72,
 	0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x02, 0x20,
@@ -437,40 +464,45 @@ var file_google_cloud_apigeeregistry_v1_style_conformance_report_proto_rawDesc =
 	0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72,
 	0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65,
 	0x2e, 0x4c, 0x69, 0x6e, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c,
-	0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd1, 0x01, 0x0a, 0x14, 0x47, 0x75, 0x69, 0x64,
-	0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70,
-	0x12, 0x50, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x35, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61,
-	0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
-	0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65,
-	0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x05, 0x73, 0x74, 0x61,
-	0x74, 0x65, 0x12, 0x67, 0x0a, 0x11, 0x67, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f,
-	0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e,
+	0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c,
+	0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
+	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07,
+	0x64, 0x6f, 0x63, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64,
+	0x6f, 0x63, 0x55, 0x72, 0x69, 0x22, 0xd1, 0x01, 0x0a, 0x14, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c,
+	0x69, 0x6e, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x50,
+	0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e,
 	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69,
 	0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x73,
-	0x74, 0x79, 0x6c, 0x65, 0x2e, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65,
-	0x70, 0x6f, 0x72, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x10, 0x67, 0x75, 0x69, 0x64, 0x65,
-	0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x0f,
-	0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12,
-	0x54, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0e, 0x32, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
-	0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
-	0x76, 0x31, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x53, 0x65,
-	0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x08, 0x73, 0x65, 0x76,
-	0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x58, 0x0a, 0x0c, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x72, 0x65,
-	0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x67, 0x6f,
+	0x74, 0x79, 0x6c, 0x65, 0x2e, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x53,
+	0x74, 0x61, 0x74, 0x65, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
+	0x12, 0x67, 0x0a, 0x11, 0x67, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x72, 0x65,
+	0x70, 0x6f, 0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x6f,
 	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65,
 	0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x79,
-	0x6c, 0x65, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x03, 0xe0,
-	0x41, 0x02, 0x52, 0x0b, 0x72, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x42,
-	0x68, 0x0a, 0x28, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c,
-	0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
-	0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x42, 0x16, 0x43, 0x6f, 0x6e,
-	0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72,
-	0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
-	0x6d, 0x2f, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
-	0x79, 0x2f, 0x72, 0x70, 0x63, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x33,
+	0x6c, 0x65, 0x2e, 0x47, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x70, 0x6f,
+	0x72, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x10, 0x67, 0x75, 0x69, 0x64, 0x65, 0x6c, 0x69,
+	0x6e, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x0f, 0x52, 0x75,
+	0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x54, 0x0a,
+	0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61,
+	0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
+	0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x2e, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x53, 0x65, 0x76, 0x65,
+	0x72, 0x69, 0x74, 0x79, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72,
+	0x69, 0x74, 0x79, 0x12, 0x58, 0x0a, 0x0c, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f,
+	0x72, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72,
+	0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65,
+	0x2e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02,
+	0x52, 0x0b, 0x72, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x42, 0x68, 0x0a,
+	0x28, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
+	0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
+	0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x42, 0x16, 0x43, 0x6f, 0x6e, 0x66, 0x6f,
+	0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74,
+	0x6f, 0x50, 0x01, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+	0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f,
+	0x72, 0x70, 0x63, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/rpc/definition.pb.go b/rpc/definition.pb.go
index a7d517e..e865242 100644
--- a/rpc/definition.pb.go
+++ b/rpc/definition.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/scoring/definition.proto
 
diff --git a/rpc/diff_analytics.pb.go b/rpc/diff_analytics.pb.go
index 0448868..9875730 100644
--- a/rpc/diff_analytics.pb.go
+++ b/rpc/diff_analytics.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/applications/v1alpha1/diff_analytics.proto
 
diff --git a/rpc/display_settings.pb.go b/rpc/display_settings.pb.go
index d4d7cad..acf5d46 100644
--- a/rpc/display_settings.pb.go
+++ b/rpc/display_settings.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/apihub/display_settings.proto
 
diff --git a/rpc/extensions.pb.go b/rpc/extensions.pb.go
index c31db14..a55de7c 100644
--- a/rpc/extensions.pb.go
+++ b/rpc/extensions.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/apihub/extensions.proto
 
diff --git a/rpc/lifecycle.pb.go b/rpc/lifecycle.pb.go
index ab56d1f..c78d061 100644
--- a/rpc/lifecycle.pb.go
+++ b/rpc/lifecycle.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/apihub/lifecycle.proto
 
diff --git a/rpc/lint.pb.go b/rpc/lint.pb.go
index 8aff823..69bd9d7 100644
--- a/rpc/lint.pb.go
+++ b/rpc/lint.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/style/lint.proto
 
diff --git a/rpc/manifest.pb.go b/rpc/manifest.pb.go
index d76dc5f..c4a3003 100644
--- a/rpc/manifest.pb.go
+++ b/rpc/manifest.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/controller/manifest.proto
 
diff --git a/rpc/provisioning_service.pb.go b/rpc/provisioning_service.pb.go
index a613475..3fa9a2f 100644
--- a/rpc/provisioning_service.pb.go
+++ b/rpc/provisioning_service.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/provisioning_service.proto
 
diff --git a/rpc/receipt.pb.go b/rpc/receipt.pb.go
index 792b9b2..4ff6a5b 100644
--- a/rpc/receipt.pb.go
+++ b/rpc/receipt.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/controller/receipt.proto
 
diff --git a/rpc/references.pb.go b/rpc/references.pb.go
index 3442d2d..5178ac5 100644
--- a/rpc/references.pb.go
+++ b/rpc/references.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/apihub/references.proto
 
diff --git a/rpc/registry_index.pb.go b/rpc/registry_index.pb.go
index 023a5c9..7fbade3 100644
--- a/rpc/registry_index.pb.go
+++ b/rpc/registry_index.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/applications/v1alpha1/registry_index.proto
 
diff --git a/rpc/registry_map.pb.go b/rpc/registry_map.pb.go
index 5e89ee7..e87366d 100644
--- a/rpc/registry_map.pb.go
+++ b/rpc/registry_map.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/internal/v1/registry_map.proto
 
diff --git a/rpc/registry_models.pb.go b/rpc/registry_models.pb.go
index 01a1fec..416af6c 100644
--- a/rpc/registry_models.pb.go
+++ b/rpc/registry_models.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/registry_models.proto
 
diff --git a/rpc/registry_notifications.pb.go b/rpc/registry_notifications.pb.go
index 3972bd6..6c4cc2b 100644
--- a/rpc/registry_notifications.pb.go
+++ b/rpc/registry_notifications.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/registry_notifications.proto
 
diff --git a/rpc/registry_references.pb.go b/rpc/registry_references.pb.go
index d1698a5..7831ca4 100644
--- a/rpc/registry_references.pb.go
+++ b/rpc/registry_references.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/applications/v1alpha1/registry_references.proto
 
diff --git a/rpc/registry_service.pb.go b/rpc/registry_service.pb.go
index 6bca422..4d0547e 100644
--- a/rpc/registry_service.pb.go
+++ b/rpc/registry_service.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/registry_service.proto
 
@@ -62,6 +62,7 @@ type ListApisRequest struct {
 	// Expression Language and can refer to all message fields.
 	Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"`
 	// A comma-separated list of fields, e.g. "foo,bar"
+	// Fields can be sorted in descending order using the "desc" identifier, e.g. "foo desc,bar"
 	OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
 }
 
@@ -473,6 +474,7 @@ type ListApiVersionsRequest struct {
 	// Expression Language and can refer to all message fields.
 	Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"`
 	// A comma-separated list of fields, e.g. "foo,bar"
+	// Fields can be sorted in descending order using the "desc" identifier, e.g. "foo desc,bar"
 	OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
 }
 
@@ -884,6 +886,7 @@ type ListApiSpecsRequest struct {
 	// Expression Language and can refer to all message fields except contents.
 	Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"`
 	// A comma-separated list of fields, e.g. "foo,bar"
+	// Fields can be sorted in descending order using the "desc" identifier, e.g. "foo desc,bar"
 	OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
 }
 
@@ -1645,6 +1648,7 @@ type ListApiDeploymentsRequest struct {
 	// Expression Language and can refer to all message fields.
 	Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"`
 	// A comma-separated list of fields, e.g. "foo,bar"
+	// Fields can be sorted in descending order using the "desc" identifier, e.g. "foo desc,bar"
 	OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
 }
 
@@ -2356,6 +2360,7 @@ type ListArtifactsRequest struct {
 	// Expression Language and can refer to all message fields except contents.
 	Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"`
 	// A comma-separated list of fields, e.g. "foo,bar"
+	// Fields can be sorted in descending order using the "desc" identifier, e.g. "foo desc,bar"
 	OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
 }
 
diff --git a/rpc/score.pb.go b/rpc/score.pb.go
index 611874f..55f0db2 100644
--- a/rpc/score.pb.go
+++ b/rpc/score.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/scoring/score.proto
 
diff --git a/rpc/score_card.pb.go b/rpc/score_card.pb.go
index ee5942b..cde6aa5 100644
--- a/rpc/score_card.pb.go
+++ b/rpc/score_card.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/scoring/score_card.proto
 
diff --git a/rpc/severity.pb.go b/rpc/severity.pb.go
index d38e482..159918a 100644
--- a/rpc/severity.pb.go
+++ b/rpc/severity.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/scoring/severity.proto
 
diff --git a/rpc/style_guide.pb.go b/rpc/style_guide.pb.go
index 1d46400..546e363 100644
--- a/rpc/style_guide.pb.go
+++ b/rpc/style_guide.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/style/style_guide.proto
 
diff --git a/rpc/taxonomies.pb.go b/rpc/taxonomies.pb.go
index 7903a32..c268e00 100644
--- a/rpc/taxonomies.pb.go
+++ b/rpc/taxonomies.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
+// 	protoc-gen-go v1.28.1
 // 	protoc        v3.21.3
 // source: google/cloud/apigeeregistry/v1/apihub/taxonomies.proto
 


Test patch:
diff --git a/cmd/registry/cmd/compute/conformance_test.go b/cmd/registry/cmd/compute/conformance_test.go
index f80c8fa..a13864f 100644
--- a/cmd/registry/cmd/compute/conformance_test.go
+++ b/cmd/registry/cmd/compute/conformance_test.go
@@ -18,6 +18,7 @@ import (
 	"bytes"
 	"compress/gzip"
 	"context"
+	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
@@ -78,8 +79,11 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_ERROR,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "norefsiblings",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "norefsiblings",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "No $ref siblings",
+												Description: "An object exposing a $ref property cannot be further extended with additional properties.",
+												DocUri:      "https://meta.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules#no-ref-siblings",
 											},
 										},
 									},
@@ -115,8 +119,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_SEVERITY_UNSPECIFIED,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "norefsiblings",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "norefsiblings",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "No $ref siblings",
+												Description: "An object exposing a $ref property cannot be further extended with additional properties.",
 											},
 										},
 									},
@@ -158,12 +164,16 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_ERROR,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "operationtags",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "operationtags",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Operation tags",
+												Description: "Operation should have non-empty tags array.",
 											},
 											{
-												RuleId: "operationtagdefined",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "operationtagdefined",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Operation tag defined",
+												Description: "Operation tags should be defined in global tags.",
 											},
 										},
 									},
@@ -172,12 +182,16 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_INFO,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "openapitags",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "openapitags",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "OpenAPI tags",
+												Description: "OpenAPI object should have non-empty tags array.",
 											},
 											{
-												RuleId: "openapitagsalphabetical",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "openapitagsalphabetical",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "OpenAPI tags alphabetical",
+												Description: "OpenAPI object should have alphabetical tags. This will be sorted by the name property.",
 											},
 										},
 									},
@@ -213,8 +227,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_ERROR,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "operationtags",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "operationtags",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Operation tags",
+												Description: "Operation should have non-empty tags array.",
 											},
 										},
 									},
@@ -223,8 +239,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_INFO,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "openapitags",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "openapitags",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "OpenAPI tags",
+												Description: "OpenAPI object should have non-empty tags array.",
 											},
 										},
 									},
@@ -244,8 +262,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_ERROR,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "norefsiblings",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "norefsiblings",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "No $ref siblings",
+												Description: "An object exposing a $ref property cannot be further extended with additional properties.",
 											},
 										},
 									},
@@ -283,12 +303,16 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_ERROR,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "operationdescription",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "operationdescription",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Operation description",
+												Description: "Operation should have non-empty description.",
 											},
 											{
-												RuleId: "infodescription",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "infodescription",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Info description",
+												Description: "OpenAPI object info description must be present and non-empty string.",
 											},
 										},
 									},
@@ -296,8 +320,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_WARNING,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "descriptiontags",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "descriptiontags",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Description tags",
+												Description: "Ensures that description fields in the OpenAPI spec contain no tags (such as HTML tags).",
 											},
 										},
 									},
@@ -305,8 +331,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_INFO,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "tagdescription",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "tagdescription",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "Tag description",
+												Description: "Tags alone are not very descriptive. Give folks a bit more information to work with.",
 											},
 										},
 									},
@@ -326,8 +354,10 @@ func TestConformance(t *testing.T) {
 										Severity: rpc.Rule_ERROR,
 										RuleReports: []*rpc.RuleReport{
 											{
-												RuleId: "norefsiblings",
-												Spec:   "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												RuleId:      "norefsiblings",
+												Spec:        "projects/conformance-test/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml",
+												DisplayName: "No $ref siblings",
+												Description: "An object exposing a $ref property cannot be further extended with additional properties.",
 											},
 										},
 									},
@@ -451,6 +481,17 @@ func TestConformance(t *testing.T) {
 				t.Fatalf("Failed getting artifact contents %s: %s", test.getPattern, err)
 			}
 
+			// Add revision ID in wantProto
+			for _, g := range test.wantProto.GuidelineReportGroups {
+				for _, gr := range g.GetGuidelineReports() {
+					for _, r := range gr.RuleReportGroups {
+						for _, report := range r.RuleReports {
+							report.Spec = fmt.Sprintf("%s@%s", report.Spec, spec.RevisionId)
+						}
+					}
+				}
+			}
+
 			gotProto := &rpc.ConformanceReport{}
 			if err := proto.Unmarshal(contents.GetData(), gotProto); err != nil {
 				t.Fatalf("Failed to unmarshal artifact: %s", err)
diff --git a/cmd/registry/cmd/compute/testdata/styleguide-default.yaml b/cmd/registry/cmd/compute/testdata/styleguide-default.yaml
index 8612b0a..f224e7e 100644
--- a/cmd/registry/cmd/compute/testdata/styleguide-default.yaml
+++ b/cmd/registry/cmd/compute/testdata/styleguide-default.yaml
@@ -21,6 +21,7 @@ guidelines:
     description: This guideline governs properties for ref fields on specs.
     rules:
       - id: norefsiblings
+        display_name: No $ref siblings
         description: An object exposing a $ref property cannot be further extended with additional properties.
         linter: sample
         linter_rulename: no-$ref-siblings
diff --git a/cmd/registry/cmd/compute/testdata/styleguide-multiple-linter.yaml b/cmd/registry/cmd/compute/testdata/styleguide-multiple-linter.yaml
index d57aba2..977e2e0 100644
--- a/cmd/registry/cmd/compute/testdata/styleguide-multiple-linter.yaml
+++ b/cmd/registry/cmd/compute/testdata/styleguide-multiple-linter.yaml
@@ -21,6 +21,7 @@ guidelines:
     description: This guideline governs properties for ref fields on specs.
     rules:
       - id: norefsiblings
+        display_name: No $ref siblings
         description: An object exposing a $ref property cannot be further extended with additional properties.
         linter: sample
         linter_rulename: no-$ref-siblings
@@ -31,21 +32,25 @@ guidelines:
     description: This guideline specifies rules for different tags.
     rules:
       - id: tagdescription
+        display_name: Tag description
         description: Tags alone are not very descriptive. Give folks a bit more information to work with.
         linter: sample
         linter_rulename: tag-description
         severity: INFO
       - id: operationdescription
+        display_name: Operation description
         description: Operation should have non-empty description.
         linter: sample
         linter_rulename: operation-description
         severity: ERROR
       - id: infodescription
+        display_name: Info description
         description: OpenAPI object info description must be present and non-empty string.
         linter: sample
         linter_rulename: info-description
         severity: ERROR
       - id: descriptiontags
+        display_name: Description tags
         description: Ensures that description fields in the OpenAPI spec contain no tags (such as HTML tags).
         linter: openapi-sample
         linter_rulename: description-contains-no-tags
diff --git a/cmd/registry/cmd/compute/testdata/styleguide-multiple-severity.yaml b/cmd/registry/cmd/compute/testdata/styleguide-multiple-severity.yaml
index c26b30b..0be0698 100644
--- a/cmd/registry/cmd/compute/testdata/styleguide-multiple-severity.yaml
+++ b/cmd/registry/cmd/compute/testdata/styleguide-multiple-severity.yaml
@@ -21,21 +21,25 @@ guidelines:
     description: This guideline specifies rules for different tags.
     rules:
       - id: openapitags
+        display_name: OpenAPI tags
         description: OpenAPI object should have non-empty tags array.
         linter: sample
         linter_rulename: openapi-tags
         severity: INFO
       - id: openapitagsalphabetical
+        display_name: OpenAPI tags alphabetical
         description: OpenAPI object should have alphabetical tags. This will be sorted by the name property.
         linter: sample
         linter_rulename: openapi-tags-alphabetical
         severity: INFO
       - id: operationtags
+        display_name: Operation tags
         description: Operation should have non-empty tags array.
         linter: sample
         linter_rulename: operation-tags
         severity: ERROR
       - id: operationtagdefined
+        display_name: Operation tag defined
         description: Operation tags should be defined in global tags.
         linter: sample
         linter_rulename: operation-tag-defined
diff --git a/cmd/registry/cmd/compute/testdata/styleguide-multiple-state.yaml b/cmd/registry/cmd/compute/testdata/styleguide-multiple-state.yaml
index 73e06ff..118aa77 100644
--- a/cmd/registry/cmd/compute/testdata/styleguide-multiple-state.yaml
+++ b/cmd/registry/cmd/compute/testdata/styleguide-multiple-state.yaml
@@ -21,6 +21,7 @@ guidelines:
     description: This guideline governs properties for ref fields on specs.
     rules:
       - id: norefsiblings
+        display_name: No $ref siblings
         description: An object exposing a $ref property cannot be further extended with additional properties.
         linter: sample
         linter_rulename: no-$ref-siblings
@@ -31,11 +32,13 @@ guidelines:
     description: This guideline specifies rules for different tags.
     rules:
       - id: openapitags
+        display_name: OpenAPI tags
         description: OpenAPI object should have non-empty tags array.
         linter: sample
         linter_rulename: openapi-tags
         severity: INFO
       - id: operationtags
+        display_name: Operation tags
         description: Operation should have non-empty tags array.
         linter: sample
         linter_rulename: operation-tags
diff --git a/cmd/registry/cmd/compute/testdata/styleguide.yaml b/cmd/registry/cmd/compute/testdata/styleguide.yaml
index a5a0ce4..be9c88a 100644
--- a/cmd/registry/cmd/compute/testdata/styleguide.yaml
+++ b/cmd/registry/cmd/compute/testdata/styleguide.yaml
@@ -21,10 +21,12 @@ guidelines:
     description: This guideline governs properties for ref fields on specs.
     rules:
       - id: norefsiblings
+        display_name: No $ref siblings
         description: An object exposing a $ref property cannot be further extended with additional properties.
         linter: sample
         linter_rulename: no-$ref-siblings
         severity: ERROR
+        doc_uri: https://meta.stoplight.io/docs/spectral/4dec24461f3af-open-api-rules#no-ref-siblings
     state: ACTIVE
 linters:
   - name: sample
diff --git a/cmd/registry/conformance/conformance-task_test.go b/cmd/registry/conformance/conformance-task_test.go
index 9d1b84f..f3ea9cd 100644
--- a/cmd/registry/conformance/conformance-task_test.go
+++ b/cmd/registry/conformance/conformance-task_test.go
@@ -27,6 +27,7 @@ import (
 
 const project = "demo"
 const specName = "projects/demo/locations/global/apis/petstore/versions/1.0.0/specs/openapi.yaml"
+const revisionId = "abcdef"
 const styleguideId = "openapi-test"
 
 // This test will catch any changes made to the original status values.
@@ -173,7 +174,7 @@ func TestComputeConformanceReport(t *testing.T) {
 										RuleReports: []*rpc.RuleReport{
 											{
 												RuleId:     "norefsiblings",
-												Spec:       specName,
+												Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 												File:       "test-result-file",
 												Suggestion: "fix no-$ref-siblings",
 												Location: &rpc.LintLocation{
@@ -255,7 +256,7 @@ func TestComputeConformanceReport(t *testing.T) {
 										RuleReports: []*rpc.RuleReport{
 											{
 												RuleId:     "operationdescription",
-												Spec:       specName,
+												Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 												File:       "test-result-file",
 												Suggestion: "fix operation-description",
 											},
@@ -377,7 +378,7 @@ func TestComputeConformanceReport(t *testing.T) {
 										RuleReports: []*rpc.RuleReport{
 											{
 												RuleId:     "norefsiblings",
-												Spec:       specName,
+												Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 												File:       "test-result-file",
 												Suggestion: "fix no-$ref-siblings",
 											},
@@ -402,7 +403,7 @@ func TestComputeConformanceReport(t *testing.T) {
 										RuleReports: []*rpc.RuleReport{
 											{
 												RuleId:     "operationdescription",
-												Spec:       specName,
+												Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 												File:       "test-result-file",
 												Suggestion: "fix operation-description",
 											},
@@ -413,13 +414,13 @@ func TestComputeConformanceReport(t *testing.T) {
 										RuleReports: []*rpc.RuleReport{
 											{
 												RuleId:     "tagdescription",
-												Spec:       specName,
+												Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 												File:       "test-result-file",
 												Suggestion: "fix tag-description",
 											},
 											{
 												RuleId:     "infodescription",
-												Spec:       specName,
+												Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 												File:       "test-result-file",
 												Suggestion: "fix info-description",
 											},
@@ -443,7 +444,10 @@ func TestComputeConformanceReport(t *testing.T) {
 			ctx := context.Background()
 
 			task := &ComputeConformanceTask{
-				Spec:         &rpc.ApiSpec{Name: specName},
+				Spec: &rpc.ApiSpec{
+					Name:       specName,
+					RevisionId: "abcdef",
+				},
 				StyleguideId: styleguideId,
 			}
 
@@ -519,7 +523,7 @@ func TestPreExistingConformanceReport(t *testing.T) {
 								RuleReports: []*rpc.RuleReport{
 									{
 										RuleId:     "tagdescription",
-										Spec:       specName,
+										Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 										File:       "test-result-file",
 										Suggestion: "fix tag-description",
 									},
@@ -560,13 +564,13 @@ func TestPreExistingConformanceReport(t *testing.T) {
 								RuleReports: []*rpc.RuleReport{
 									{
 										RuleId:     "tagdescription",
-										Spec:       specName,
+										Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 										File:       "test-result-file",
 										Suggestion: "fix tag-description",
 									},
 									{
 										RuleId:     "operationdescription",
-										Spec:       specName,
+										Spec:       fmt.Sprintf("%s@%s", specName, revisionId),
 										File:       "test-result-file",
 										Suggestion: "fix operation-description",
 									},
@@ -587,7 +591,10 @@ func TestPreExistingConformanceReport(t *testing.T) {
 	ctx := context.Background()
 
 	task := &ComputeConformanceTask{
-		Spec:         &rpc.ApiSpec{Name: specName},
+		Spec: &rpc.ApiSpec{
+			Name:       specName,
+			RevisionId: revisionId,
+		},
 		StyleguideId: styleguideId,
 	}