Repository: googleapis/api-linter
Pull request #343 (2019-11-14 00:10:27)

[refactor] Shorten documentation URLs.
This moves our documentation URLs for individual rules to:

    https://linter.aip.dev/{aip}/{rule_name}

For example:

    https://linter.aip.dev/122/camel-case-uris

It also adds an index page for each AIP, so that every parent
path is reachable and sufficiently pretty.


Patch:
diff --git a/.circleci/meta-linter/rules_documented.go b/.circleci/meta-linter/rules_documented.go
index 44ceae8..69f47be 100644
--- a/.circleci/meta-linter/rules_documented.go
+++ b/.circleci/meta-linter/rules_documented.go
@@ -38,7 +38,7 @@ func checkRulesDocumented() (errors []error) {
 		}
 
 		// Determine the expected path for documentation.
-		wantFile := fmt.Sprintf("docs/rules/core/%s-%s.md", match[1], strings.ReplaceAll(match[2], "_", "-"))
+		wantFile := fmt.Sprintf("docs/rules/%s/%s.md", match[1], strings.ReplaceAll(match[2], "_", "-"))
 		if _, err := ioutil.ReadFile(wantFile); err != nil {
 			errors = append(errors, fmt.Errorf("missing rule documentation: %s", wantFile))
 			return nil
diff --git a/docs/_includes/linter-aip-listing.md b/docs/_includes/linter-aip-listing.md
new file mode 100644
index 0000000..82baba9
--- /dev/null
+++ b/docs/_includes/linter-aip-listing.md
@@ -0,0 +1,32 @@
+{% comment %} When this file is included, it _must_ be sent an `aip` value.
+This determines which rules are rendered.{% endcomment -%}
+
+Rules for [AIP-{{include.aip}}](), covering
+{{ page.prose_title | default: page.title | downcase }}.
+
+<!-- prettier-ignore -->
+<div class="aip-rule-listing">
+  {% assign rule_pages = site.pages | where_exp: "p", "p.rule != nil" | where_exp: "p", "p.rule.aip == include.aip" | sort: "rule.name" -%}
+  <table class="glue-table--datatable glue-table--stacked api-linter-rule-listing" style="width: 100%;">
+    <tr>
+      <th>Rule name</th>
+      <th>Description</th>
+    </tr>
+    {% for p in rule_pages -%}
+    <tr>
+      <td style="vertical-align: top;">
+        <a href="{{ site.url }}{{ p.url }}">
+        <tt>{{ p.rule.name | last }}</tt>
+        </a>
+      </td>
+      <td>{{ p.rule.summary }}</td>
+    </tr>
+    {% endfor -%}
+  </table>
+</div>
+
+**Note:** Because AIPs sometimes cover topics that have some overlap, some
+rules related to {{ page.prose_title | default: page.title | downcase }} may be
+included in the rules for other AIPs.
+
+[aip-{{include.aip}}]: https://aip.dev/{{ includes.aip }}
diff --git a/docs/_includes/linter-breadcrumb.html b/docs/_includes/linter-breadcrumb.html
index 8a7a068..ba7e3e1 100644
--- a/docs/_includes/linter-breadcrumb.html
+++ b/docs/_includes/linter-breadcrumb.html
@@ -11,6 +11,18 @@
     </li>
     {%- if (page.url | slice: 0, 7) == '/rules/' %}
     {%- assign url_segments = page.url | split: '/' %}
+    {%- if url_segments | first | abs %}
+    <li class="glue-breadcrumbs__item" aria-level="2">
+      <a class="glue-breadcrumbs__link" href="{{ site.url }}/rules/">
+        Rule Documentation
+      </a>
+    </li>
+    <li class="glue-breadcrumbs__item" aria-level="2">
+      <a class="glue-breadcrumbs__link" href="{{ site.url }}/rules/core/">
+        Core rules
+      </a>
+    </li>
+    {%- endif %}
     {%- assign url_limit = url_segments | size | minus: 2 %}
     {%- assign cursor = '' %}
     {%- for segment in url_segments offset: 1 limit: url_limit %}
@@ -21,6 +33,8 @@
           Rule Documentation
           {%- elsif segment == "core" %}
           Core rules
+          {%- elsif segment | abs %}
+          AIP-{{ segment }}
           {%- else %}
           {{ segment | replace: ('-', ' ') | capitalize }}
           {% endif %}
@@ -32,7 +46,7 @@
       class="glue-breadcrumbs__item glue-breadcrumbs__item--active"
       aria-level="3"
     >
-      {{ page.title }}
+      {% if page.aip_listing %}AIP-{{ page.aip_listing }}{% else %}{{ page.title }}{% endif %}
     </li>
   </ol>
 </nav>
diff --git a/docs/_includes/linter-rule-listing.html b/docs/_includes/linter-group-listing.html
similarity index 95%
rename from docs/_includes/linter-rule-listing.html
rename to docs/_includes/linter-group-listing.html
index 231c303..e5b4d75 100644
--- a/docs/_includes/linter-rule-listing.html
+++ b/docs/_includes/linter-group-listing.html
@@ -21,7 +21,7 @@ values are inclusive. {% endcomment -%}
     </svg>
   </div>
 
-  {% assign rule_pages = site.pages | where_exp: "p", "p.rule != nil" | group_by: "rule.aip" -%}
+  {% assign rule_pages = site.pages | where_exp: "p", "p.rule != nil" | sort: "rule.name" | group_by: "rule.aip" -%}
   {% for pbr in rule_pages -%}
   <div class="glue-expansion-panel">
     <div class="glue-expansion-panel-toggle"
@@ -41,7 +41,7 @@ values are inclusive. {% endcomment -%}
           <tr>
             <td style="vertical-align: top;">
               <a href="{{ p.url | remove_first: '/' }}">
-              <tt>{{ p.rule.name | join: '::' }}</tt>
+              <tt>{{ p.rule.name | last }}</tt>
               </a>
             </td>
             <td>{{ p.rule.summary }}</td>
diff --git a/docs/_sass/api-linter/tables.scss b/docs/_sass/api-linter/tables.scss
index 8b5da56..23c6ff2 100644
--- a/docs/_sass/api-linter/tables.scss
+++ b/docs/_sass/api-linter/tables.scss
@@ -23,3 +23,8 @@
 .glue-expansion-panels .glue-expansion-panel-content > div.has-rule-listing {
   padding: 0px;
 }
+
+div.aip-rule-listing {
+  margin-top: 40px;
+  margin-bottom: 40px;
+}
diff --git a/docs/rules/core/0122-camel-case-uris.md b/docs/rules/0122/camel-case-uris.md
similarity index 95%
rename from docs/rules/core/0122-camel-case-uris.md
rename to docs/rules/0122/camel-case-uris.md
index 1c583cf..04293de 100644
--- a/docs/rules/core/0122-camel-case-uris.md
+++ b/docs/rules/0122/camel-case-uris.md
@@ -3,6 +3,9 @@ rule:
   aip: 122
   name: [core, '0122', camel-case-uris]
   summary: All resource names must use camel case in collection identifiers.
+permalink: /122/camel-case-uris
+redirect_from:
+  - /0122/camel-case-uris
 ---
 
 # HTTP URI case
diff --git a/docs/rules/0122/index.md b/docs/rules/0122/index.md
new file mode 100644
index 0000000..10a746a
--- /dev/null
+++ b/docs/rules/0122/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 122
+permalink: /122/
+redirect_from:
+  - /0122/
+---
+
+# Resource names
+
+{% include linter-aip-listing.md aip=122 %}
diff --git a/docs/rules/0126/index.md b/docs/rules/0126/index.md
new file mode 100644
index 0000000..f7977d7
--- /dev/null
+++ b/docs/rules/0126/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 126
+permalink: /126/
+redirect_from:
+  - /0126/
+---
+
+# Enumerations
+
+{% include linter-aip-listing.md aip=126 %}
diff --git a/docs/rules/core/0126-unspecified.md b/docs/rules/0126/unspecified.md
similarity index 96%
rename from docs/rules/core/0126-unspecified.md
rename to docs/rules/0126/unspecified.md
index 7ab0e87..90e7f8c 100644
--- a/docs/rules/core/0126-unspecified.md
+++ b/docs/rules/0126/unspecified.md
@@ -3,6 +3,9 @@ rule:
   aip: 126
   name: [core, '0126', unspecified]
   summary: All enums must have a default unspecified value.
+permalink: /126/unspecified
+redirect_from:
+  - /0126/unspecified
 ---
 
 # Enum unspecified value
diff --git a/docs/rules/core/0126-upper-snake-values.md b/docs/rules/0126/upper-snake-values.md
similarity index 92%
rename from docs/rules/core/0126-upper-snake-values.md
rename to docs/rules/0126/upper-snake-values.md
index 97a66aa..62e7b13 100644
--- a/docs/rules/core/0126-upper-snake-values.md
+++ b/docs/rules/0126/upper-snake-values.md
@@ -3,6 +3,9 @@ rule:
   aip: 126
   name: [core, '0126', upper-snake-values]
   summary: All enum values must be in upper snake case.
+permalink: /126/upper-snake-values
+redirect_from:
+  - /0126/upper-snake-values
 ---
 
 # Upper snake case values
diff --git a/docs/rules/core/0127-http-annotation.md b/docs/rules/0127/http-annotation.md
similarity index 95%
rename from docs/rules/core/0127-http-annotation.md
rename to docs/rules/0127/http-annotation.md
index bb65bb1..851dfe9 100644
--- a/docs/rules/core/0127-http-annotation.md
+++ b/docs/rules/0127/http-annotation.md
@@ -3,6 +3,9 @@ rule:
   aip: 127
   name: [core, '0127', http-annotation]
   summary: HTTP annotations must be present on non-streaming methods.
+permalink: /127/http-annotation
+redirect_from:
+  - /0127/http-annotation
 ---
 
 # HTTP URI case
diff --git a/docs/rules/0127/index.md b/docs/rules/0127/index.md
new file mode 100644
index 0000000..bd16be5
--- /dev/null
+++ b/docs/rules/0127/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 127
+permalink: /127/
+redirect_from:
+  - /0127/
+---
+
+# HTTP and gRPC Transcoding
+
+{% include linter-aip-listing.md aip=127 %}
diff --git a/docs/rules/core/0131-http-body.md b/docs/rules/0131/http-body.md
similarity index 96%
rename from docs/rules/core/0131-http-body.md
rename to docs/rules/0131/http-body.md
index fc262ee..b2604b1 100644
--- a/docs/rules/core/0131-http-body.md
+++ b/docs/rules/0131/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', http-body]
   summary: Get methods must not have an HTTP body.
+permalink: /131/http-body
+redirect_from:
+  - /0131/http-body
 ---
 
 # Get methods: No HTTP body
diff --git a/docs/rules/core/0131-http-method.md b/docs/rules/0131/http-method.md
similarity index 95%
rename from docs/rules/core/0131-http-method.md
rename to docs/rules/0131/http-method.md
index 78263ed..c470631 100644
--- a/docs/rules/core/0131-http-method.md
+++ b/docs/rules/0131/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', http-method]
   summary: Get methods must use the GET HTTP verb.
+permalink: /131/http-method
+redirect_from:
+  - /0131/http-method
 ---
 
 # Get methods: GET HTTP verb
diff --git a/docs/rules/core/0131-http-uri-name.md b/docs/rules/0131/http-uri-name.md
similarity index 95%
rename from docs/rules/core/0131-http-uri-name.md
rename to docs/rules/0131/http-uri-name.md
index c88c95f..eef3fca 100644
--- a/docs/rules/core/0131-http-uri-name.md
+++ b/docs/rules/0131/http-uri-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', http-uri-name]
   summary: Get methods must map the name field to the URI.
+permalink: /131/http-uri-name
+redirect_from:
+  - /0131/http-uri-name
 ---
 
 # Get methods: HTTP URI name field
diff --git a/docs/rules/0131/index.md b/docs/rules/0131/index.md
new file mode 100644
index 0000000..8f6ee0e
--- /dev/null
+++ b/docs/rules/0131/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 131
+permalink: /131/
+redirect_from:
+  - /0131/
+prose_title: get methods
+---
+
+# Standard methods: Get
+
+{% include linter-aip-listing.md aip=131 %}
diff --git a/docs/rules/core/0131-request-message-name.md b/docs/rules/0131/request-message-name.md
similarity index 94%
rename from docs/rules/core/0131-request-message-name.md
rename to docs/rules/0131/request-message-name.md
index 6c761b0..ae39144 100644
--- a/docs/rules/core/0131-request-message-name.md
+++ b/docs/rules/0131/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', request-message-name]
   summary: Get methods must have standardized request message names.
+permalink: /131/request-message-name
+redirect_from:
+  - /0131/request-message-name
 ---
 
 # Get methods: Request message
diff --git a/docs/rules/core/0131-request-name-field.md b/docs/rules/0131/request-name-field.md
similarity index 94%
rename from docs/rules/core/0131-request-name-field.md
rename to docs/rules/0131/request-name-field.md
index f10f2b8..1dbb7ac 100644
--- a/docs/rules/core/0131-request-name-field.md
+++ b/docs/rules/0131/request-name-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', request-name-field]
   summary: Get RPCs must have a `name` field in the request.
+permalink: /131/request-name-field
+redirect_from:
+  - /0131/request-name-field
 ---
 
 # Get methods: Name field
diff --git a/docs/rules/core/0131-request-unknown-fields.md b/docs/rules/0131/request-unknown-fields.md
similarity index 94%
rename from docs/rules/core/0131-request-unknown-fields.md
rename to docs/rules/0131/request-unknown-fields.md
index 13c05e6..ed44c5d 100644
--- a/docs/rules/core/0131-request-unknown-fields.md
+++ b/docs/rules/0131/request-unknown-fields.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', request-unknown-fields]
   summary: Get RPCs should not have unexpected fields in the request.
+permalink: /131/request-unknown-fields
+redirect_from:
+  - /0131/request-unknown-fields
 ---
 
 # Get methods: Unknown fields
diff --git a/docs/rules/core/0131-response-message-name.md b/docs/rules/0131/response-message-name.md
similarity index 94%
rename from docs/rules/core/0131-response-message-name.md
rename to docs/rules/0131/response-message-name.md
index ef3314e..6f84de3 100644
--- a/docs/rules/core/0131-response-message-name.md
+++ b/docs/rules/0131/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', response-message-name]
   summary: Get methods must return the resource.
+permalink: /131/response-message-name
+redirect_from:
+  - /0131/response-message-name
 ---
 
 # Upper snake case values
diff --git a/docs/rules/core/0131-synonyms.md b/docs/rules/0131/synonyms.md
similarity index 96%
rename from docs/rules/core/0131-synonyms.md
rename to docs/rules/0131/synonyms.md
index 5af2eab..953cf7b 100644
--- a/docs/rules/core/0131-synonyms.md
+++ b/docs/rules/0131/synonyms.md
@@ -3,6 +3,9 @@ rule:
   aip: 131
   name: [core, '0131', synonyms]
   summary: Get methods must be named starting with "Get".
+permalink: /131/synonyms
+redirect_from:
+  - /0131/synonyms
 ---
 
 # Get methods: Synonym check
diff --git a/docs/rules/0132/index.md b/docs/rules/0132/index.md
new file mode 100644
index 0000000..b175c1d
--- /dev/null
+++ b/docs/rules/0132/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 132
+permalink: /132/
+redirect_from:
+  - /0132/
+prose_title: list methods
+---
+
+# Standard methods: List
+
+{% include linter-aip-listing.md aip=132 %}
diff --git a/docs/rules/core/0132-request-field-types.md b/docs/rules/0132/request-field-types.md
similarity index 94%
rename from docs/rules/core/0132-request-field-types.md
rename to docs/rules/0132/request-field-types.md
index 8247f45..b8d1cea 100644
--- a/docs/rules/core/0132-request-field-types.md
+++ b/docs/rules/0132/request-field-types.md
@@ -3,6 +3,9 @@ rule:
   aip: 132
   name: [core, '0132', request-field-types]
   summary: List RPCs should have fields with consistent types.
+permalink: /132/request-field-types
+redirect_from:
+  - /0132/request-field-types
 ---
 
 # Get methods: Unknown fields
diff --git a/docs/rules/core/0132-request-message-name.md b/docs/rules/0132/request-message-name.md
similarity index 95%
rename from docs/rules/core/0132-request-message-name.md
rename to docs/rules/0132/request-message-name.md
index 6cdc865..a89eb54 100644
--- a/docs/rules/core/0132-request-message-name.md
+++ b/docs/rules/0132/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 132
   name: [core, '0132', request-message-name]
   summary: List methods must have standardized request message names.
+permalink: /132/request-message-name
+redirect_from:
+  - /0132/request-message-name
 ---
 
 # List methods: Request message
diff --git a/docs/rules/core/0132-request-parent-field.md b/docs/rules/0132/request-parent-field.md
similarity index 95%
rename from docs/rules/core/0132-request-parent-field.md
rename to docs/rules/0132/request-parent-field.md
index 206de28..a7087ea 100644
--- a/docs/rules/core/0132-request-parent-field.md
+++ b/docs/rules/0132/request-parent-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 132
   name: [core, '0132', request-parent-field]
   summary: List RPCs must have a `parent` field in the request.
+permalink: /132/request-parent-field
+redirect_from:
+  - /0132/request-parent-field
 ---
 
 # List methods: Parent field
diff --git a/docs/rules/core/0132-request-unknown-fields.md b/docs/rules/0132/request-unknown-fields.md
similarity index 95%
rename from docs/rules/core/0132-request-unknown-fields.md
rename to docs/rules/0132/request-unknown-fields.md
index c271017..f5b7cfa 100644
--- a/docs/rules/core/0132-request-unknown-fields.md
+++ b/docs/rules/0132/request-unknown-fields.md
@@ -3,6 +3,9 @@ rule:
   aip: 132
   name: [core, '0132', request-unknown-fields]
   summary: List RPCs should not have unexpected fields in the request.
+permalink: /132/request-unknown-fields
+redirect_from:
+  - /0132/request-unknown-fields
 ---
 
 # Get methods: Unknown fields
diff --git a/docs/rules/core/0132-response-message-name.md b/docs/rules/0132/response-message-name.md
similarity index 94%
rename from docs/rules/core/0132-response-message-name.md
rename to docs/rules/0132/response-message-name.md
index 3edc9e8..df6ecc5 100644
--- a/docs/rules/core/0132-response-message-name.md
+++ b/docs/rules/0132/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 132
   name: [core, '0132', response-message-name]
   summary: List methods must have standardized response message names.
+permalink: /132/response-message-name
+redirect_from:
+  - /0132/response-message-name
 ---
 
 # List methods: Response message
diff --git a/docs/rules/core/0133-http-body.md b/docs/rules/0133/http-body.md
similarity index 96%
rename from docs/rules/core/0133-http-body.md
rename to docs/rules/0133/http-body.md
index c103214..91b5f27 100644
--- a/docs/rules/core/0133-http-body.md
+++ b/docs/rules/0133/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', http-body]
   summary: Create methods must have the HTTP body set to the resource.
+permalink: /133/http-body
+redirect_from:
+  - /0133/http-body
 ---
 
 # Create methods: HTTP body
diff --git a/docs/rules/core/0133-http-method.md b/docs/rules/0133/http-method.md
similarity index 95%
rename from docs/rules/core/0133-http-method.md
rename to docs/rules/0133/http-method.md
index 5aca639..2b6630e 100644
--- a/docs/rules/core/0133-http-method.md
+++ b/docs/rules/0133/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', http-method]
   summary: Create methods must use the POST HTTP verb.
+permalink: /133/http-method
+redirect_from:
+  - /0133/http-method
 ---
 
 # Create methods: POST HTTP verb
diff --git a/docs/rules/core/0133-http-uri-parent.md b/docs/rules/0133/http-uri-parent.md
similarity index 95%
rename from docs/rules/core/0133-http-uri-parent.md
rename to docs/rules/0133/http-uri-parent.md
index fc48637..816ba04 100644
--- a/docs/rules/core/0133-http-uri-parent.md
+++ b/docs/rules/0133/http-uri-parent.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', http-uri-parent]
   summary: Create methods must map the parent field to the URI.
+permalink: /133/http-uri-parent
+redirect_from:
+  - /0133/http-uri-parent
 ---
 
 # Create methods: HTTP URI parent field
diff --git a/docs/rules/0133/index.md b/docs/rules/0133/index.md
new file mode 100644
index 0000000..467f2a2
--- /dev/null
+++ b/docs/rules/0133/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 133
+permalink: /133/
+redirect_from:
+  - /0133/
+prose_title: create methods
+---
+
+# Standard methods: Create
+
+{% include linter-aip-listing.md aip=133 %}
diff --git a/docs/rules/core/0133-request-message-name.md b/docs/rules/0133/request-message-name.md
similarity index 95%
rename from docs/rules/core/0133-request-message-name.md
rename to docs/rules/0133/request-message-name.md
index ce85142..619a18c 100644
--- a/docs/rules/core/0133-request-message-name.md
+++ b/docs/rules/0133/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', request-message-name]
   summary: Create methods must have standardized request message names.
+permalink: /133/request-message-name
+redirect_from:
+  - /0133/request-message-name
 ---
 
 # Create methods: Request message
diff --git a/docs/rules/core/0133-request-parent-field.md b/docs/rules/0133/request-parent-field.md
similarity index 95%
rename from docs/rules/core/0133-request-parent-field.md
rename to docs/rules/0133/request-parent-field.md
index 1a29067..75646a9 100644
--- a/docs/rules/core/0133-request-parent-field.md
+++ b/docs/rules/0133/request-parent-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', request-parent-field]
   summary: Create RPCs must have a `parent` field in the request.
+permalink: /133/request-parent-field
+redirect_from:
+  - /0133/request-parent-field
 ---
 
 # Create methods: Parent field
diff --git a/docs/rules/core/0133-request-resource-field.md b/docs/rules/0133/request-resource-field.md
similarity index 94%
rename from docs/rules/core/0133-request-resource-field.md
rename to docs/rules/0133/request-resource-field.md
index 565fe8e..8d1dcc6 100644
--- a/docs/rules/core/0133-request-resource-field.md
+++ b/docs/rules/0133/request-resource-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', request-resource-field]
   summary: Create RPCs must have a field for the resource in the request.
+permalink: /133/request-resource-field
+redirect_from:
+  - /0133/request-resource-field
 ---
 
 # Create methods: Resource field
diff --git a/docs/rules/core/0133-request-unknown-fields.md b/docs/rules/0133/request-unknown-fields.md
similarity index 94%
rename from docs/rules/core/0133-request-unknown-fields.md
rename to docs/rules/0133/request-unknown-fields.md
index 50a9300..ede2eee 100644
--- a/docs/rules/core/0133-request-unknown-fields.md
+++ b/docs/rules/0133/request-unknown-fields.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', request-unknown-fields]
   summary: Create RPCs should not have unexpected fields in the request.
+permalink: /133/request-unknown-fields
+redirect_from:
+  - /0133/request-unknown-fields
 ---
 
 # Create methods: Unknown fields
diff --git a/docs/rules/core/0133-response-message-name.md b/docs/rules/0133/response-message-name.md
similarity index 96%
rename from docs/rules/core/0133-response-message-name.md
rename to docs/rules/0133/response-message-name.md
index 65c22fc..386a1b2 100644
--- a/docs/rules/core/0133-response-message-name.md
+++ b/docs/rules/0133/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', response-message-name]
   summary: Create methods must return the resource.
+permalink: /133/response-message-name
+redirect_from:
+  - /0133/response-message-name
 ---
 
 # Create methods: Resource response message
diff --git a/docs/rules/core/0133-synonyms.md b/docs/rules/0133/synonyms.md
similarity index 96%
rename from docs/rules/core/0133-synonyms.md
rename to docs/rules/0133/synonyms.md
index 3c0bd40..4dd3c51 100644
--- a/docs/rules/core/0133-synonyms.md
+++ b/docs/rules/0133/synonyms.md
@@ -3,6 +3,9 @@ rule:
   aip: 133
   name: [core, '0133', synonyms]
   summary: Create methods must be named starting with "Create".
+permalink: /133/synonyms
+redirect_from:
+  - /0133/synonyms
 ---
 
 # Create methods: Synonym check
diff --git a/docs/rules/core/0134-http-body.md b/docs/rules/0134/http-body.md
similarity index 96%
rename from docs/rules/core/0134-http-body.md
rename to docs/rules/0134/http-body.md
index d85aee6..af01e1f 100644
--- a/docs/rules/core/0134-http-body.md
+++ b/docs/rules/0134/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', http-body]
   summary: Update methods must have the HTTP body set to the resource.
+permalink: /134/http-body
+redirect_from:
+  - /0134/http-body
 ---
 
 # Update methods: HTTP body
diff --git a/docs/rules/core/0134-http-method.md b/docs/rules/0134/http-method.md
similarity index 96%
rename from docs/rules/core/0134-http-method.md
rename to docs/rules/0134/http-method.md
index 652b8ea..c828e40 100644
--- a/docs/rules/core/0134-http-method.md
+++ b/docs/rules/0134/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', http-method]
   summary: Update methods must use the PATCH HTTP verb.
+permalink: /134/http-method
+redirect_from:
+  - /0134/http-method
 ---
 
 # Update methods: PATCH HTTP verb
diff --git a/docs/rules/core/0134-http-uri-name.md b/docs/rules/0134/http-uri-name.md
similarity index 96%
rename from docs/rules/core/0134-http-uri-name.md
rename to docs/rules/0134/http-uri-name.md
index 58ab938..89b60a8 100644
--- a/docs/rules/core/0134-http-uri-name.md
+++ b/docs/rules/0134/http-uri-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', http-uri-name]
   summary: Update methods must map the resource's name field to the URI.
+permalink: /134/http-uri-name
+redirect_from:
+  - /0134/http-uri-name
 ---
 
 # Update methods: HTTP URI name field
diff --git a/docs/rules/0134/index.md b/docs/rules/0134/index.md
new file mode 100644
index 0000000..6fbcc2e
--- /dev/null
+++ b/docs/rules/0134/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 134
+permalink: /134/
+redirect_from:
+  - /0134/
+prose_title: update methods
+---
+
+# Standard methods: Update
+
+{% include linter-aip-listing.md aip=134 %}
diff --git a/docs/rules/core/0134-request-mask-field.md b/docs/rules/0134/request-mask-field.md
similarity index 94%
rename from docs/rules/core/0134-request-mask-field.md
rename to docs/rules/0134/request-mask-field.md
index 2266472..97a2d80 100644
--- a/docs/rules/core/0134-request-mask-field.md
+++ b/docs/rules/0134/request-mask-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', request-mask-field]
   summary: Update RPCs must have a field mask in the request.
+permalink: /134/request-mask-field
+redirect_from:
+  - /0134/request-mask-field
 ---
 
 # Update methods: Mask field
diff --git a/docs/rules/core/0134-request-message-name.md b/docs/rules/0134/request-message-name.md
similarity index 95%
rename from docs/rules/core/0134-request-message-name.md
rename to docs/rules/0134/request-message-name.md
index 18b6e2d..69bd636 100644
--- a/docs/rules/core/0134-request-message-name.md
+++ b/docs/rules/0134/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', request-message-name]
   summary: Update methods must have standardized request message names.
+permalink: /134/request-message-name
+redirect_from:
+  - /0134/request-message-name
 ---
 
 # Update methods: Request message
diff --git a/docs/rules/core/0134-request-resource-field.md b/docs/rules/0134/request-resource-field.md
similarity index 94%
rename from docs/rules/core/0134-request-resource-field.md
rename to docs/rules/0134/request-resource-field.md
index 380a06b..e640af1 100644
--- a/docs/rules/core/0134-request-resource-field.md
+++ b/docs/rules/0134/request-resource-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', request-resource-field]
   summary: Update RPCs must have a field for the resource in the request.
+permalink: /134/request-resource-field
+redirect_from:
+  - /0134/request-resource-field
 ---
 
 # Update methods: Resource field
diff --git a/docs/rules/core/0134-request-unknown-fields.md b/docs/rules/0134/request-unknown-fields.md
similarity index 94%
rename from docs/rules/core/0134-request-unknown-fields.md
rename to docs/rules/0134/request-unknown-fields.md
index f3226f9..6c3985c 100644
--- a/docs/rules/core/0134-request-unknown-fields.md
+++ b/docs/rules/0134/request-unknown-fields.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', request-unknown-fields]
   summary: Update RPCs should not have unexpected fields in the request.
+permalink: /134/request-unknown-fields
+redirect_from:
+  - /0134/request-unknown-fields
 ---
 
 # Update methods: Unknown fields
diff --git a/docs/rules/core/0134-response-message-name.md b/docs/rules/0134/response-message-name.md
similarity index 96%
rename from docs/rules/core/0134-response-message-name.md
rename to docs/rules/0134/response-message-name.md
index 023edc0..5f47699 100644
--- a/docs/rules/core/0134-response-message-name.md
+++ b/docs/rules/0134/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', response-message-name]
   summary: Update methods must return the resource.
+permalink: /134/response-message-name
+redirect_from:
+  - /0134/response-message-name
 ---
 
 # Update methods: Resource response message
diff --git a/docs/rules/core/0134-synonyms.md b/docs/rules/0134/synonyms.md
similarity index 96%
rename from docs/rules/core/0134-synonyms.md
rename to docs/rules/0134/synonyms.md
index 0041baa..abc2588 100644
--- a/docs/rules/core/0134-synonyms.md
+++ b/docs/rules/0134/synonyms.md
@@ -3,6 +3,9 @@ rule:
   aip: 134
   name: [core, '0134', synonyms]
   summary: Update methods must be named starting with "Update".
+permalink: /134/synonyms
+redirect_from:
+  - /0134/synonyms
 ---
 
 # Update methods: Synonym check
diff --git a/docs/rules/core/0135-http-body.md b/docs/rules/0135/http-body.md
similarity index 96%
rename from docs/rules/core/0135-http-body.md
rename to docs/rules/0135/http-body.md
index 0edd2fd..969d5cc 100644
--- a/docs/rules/core/0135-http-body.md
+++ b/docs/rules/0135/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', http-body]
   summary: Delete methods must not have an HTTP body.
+permalink: /135/http-body
+redirect_from:
+  - /0135/http-body
 ---
 
 # Delete methods: No HTTP body
diff --git a/docs/rules/core/0135-http-method.md b/docs/rules/0135/http-method.md
similarity index 95%
rename from docs/rules/core/0135-http-method.md
rename to docs/rules/0135/http-method.md
index d7ffe42..a1a36e0 100644
--- a/docs/rules/core/0135-http-method.md
+++ b/docs/rules/0135/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', http-method]
   summary: Delete methods must use the DELETE HTTP verb.
+permalink: /135/http-method
+redirect_from:
+  - /0135/http-method
 ---
 
 # Delete methods: DELETE HTTP verb
diff --git a/docs/rules/core/0135-http-uri-name.md b/docs/rules/0135/http-uri-name.md
similarity index 95%
rename from docs/rules/core/0135-http-uri-name.md
rename to docs/rules/0135/http-uri-name.md
index 55a8023..1a9d13c 100644
--- a/docs/rules/core/0135-http-uri-name.md
+++ b/docs/rules/0135/http-uri-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', http-uri-name]
   summary: Delete methods must map the name field to the URI.
+permalink: /135/http-uri-name
+redirect_from:
+  - /0135/http-uri-name
 ---
 
 # Delete methods: HTTP URI name field
diff --git a/docs/rules/0135/index.md b/docs/rules/0135/index.md
new file mode 100644
index 0000000..59d0997
--- /dev/null
+++ b/docs/rules/0135/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 135
+permalink: /135/
+redirect_from:
+  - /0135/
+prose_title: delete methods
+---
+
+# Standard methods: Delete
+
+{% include linter-aip-listing.md aip=135 %}
diff --git a/docs/rules/core/0135-request-message-name.md b/docs/rules/0135/request-message-name.md
similarity index 95%
rename from docs/rules/core/0135-request-message-name.md
rename to docs/rules/0135/request-message-name.md
index aec794d..acece71 100644
--- a/docs/rules/core/0135-request-message-name.md
+++ b/docs/rules/0135/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', request-message-name]
   summary: Delete methods must have standardized request message names.
+permalink: /135/request-message-name
+redirect_from:
+  - /0135/request-message-name
 ---
 
 # Delete methods: Request message
diff --git a/docs/rules/core/0135-request-name-field.md b/docs/rules/0135/request-name-field.md
similarity index 94%
rename from docs/rules/core/0135-request-name-field.md
rename to docs/rules/0135/request-name-field.md
index 811feac..9e1fd7f 100644
--- a/docs/rules/core/0135-request-name-field.md
+++ b/docs/rules/0135/request-name-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', request-name-field]
   summary: Delete RPCs must have a `name` field in the request.
+permalink: /135/request-name-field
+redirect_from:
+  - /0135/request-name-field
 ---
 
 # Delete methods: Name field
diff --git a/docs/rules/core/0135-request-unknown-fields.md b/docs/rules/0135/request-unknown-fields.md
similarity index 94%
rename from docs/rules/core/0135-request-unknown-fields.md
rename to docs/rules/0135/request-unknown-fields.md
index 1478cef..b6335b1 100644
--- a/docs/rules/core/0135-request-unknown-fields.md
+++ b/docs/rules/0135/request-unknown-fields.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', request-unknown-fields]
   summary: Delete RPCs should not have unexpected fields in the request.
+permalink: /135/request-unknown-fields
+redirect_from:
+  - /0135/request-unknown-fields
 ---
 
 # Delete methods: Unknown fields
diff --git a/docs/rules/core/0135-response-message-name.md b/docs/rules/0135/response-message-name.md
similarity index 97%
rename from docs/rules/core/0135-response-message-name.md
rename to docs/rules/0135/response-message-name.md
index 5d29899..fdafd18 100644
--- a/docs/rules/core/0135-response-message-name.md
+++ b/docs/rules/0135/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 135
   name: [core, '0135', response-message-name]
   summary: Delete methods must return Empty or the resource.
+permalink: /135/response-message-name
+redirect_from:
+  - /0135/response-message-name
 ---
 
 # Delete methods: Empty response message
diff --git a/docs/rules/core/0136-http-body.md b/docs/rules/0136/http-body.md
similarity index 96%
rename from docs/rules/core/0136-http-body.md
rename to docs/rules/0136/http-body.md
index 06e7afb..73ca019 100644
--- a/docs/rules/core/0136-http-body.md
+++ b/docs/rules/0136/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 136
   name: [core, '0136', http-body]
   summary: Custom methods must have the HTTP body set to `*`.
+permalink: /136/http-body
+redirect_from:
+  - /0136/http-body
 ---
 
 # Custom methods: HTTP body
diff --git a/docs/rules/core/0136-http-method.md b/docs/rules/0136/http-method.md
similarity index 96%
rename from docs/rules/core/0136-http-method.md
rename to docs/rules/0136/http-method.md
index ad91bd5..5f33d83 100644
--- a/docs/rules/core/0136-http-method.md
+++ b/docs/rules/0136/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 136
   name: [core, '0136', http-method]
   summary: Custom methods must use the POST or GET HTTP verb.
+permalink: /136/http-method
+redirect_from:
+  - /0136/http-method
 ---
 
 # Custom methods: HTTP method
diff --git a/docs/rules/core/0136-http-uri-suffix.md b/docs/rules/0136/http-uri-suffix.md
similarity index 97%
rename from docs/rules/core/0136-http-uri-suffix.md
rename to docs/rules/0136/http-uri-suffix.md
index 3757004..cd51fad 100644
--- a/docs/rules/core/0136-http-uri-suffix.md
+++ b/docs/rules/0136/http-uri-suffix.md
@@ -3,6 +3,9 @@ rule:
   aip: 136
   name: [core, '0136', http-uri-suffix]
   summary: Custom methods should have a correct URI suffix.
+permalink: /136/http-uri-suffix
+redirect_from:
+  - /0136/http-uri-suffix
 ---
 
 # Custom methods: URI suffix
diff --git a/docs/rules/0136/index.md b/docs/rules/0136/index.md
new file mode 100644
index 0000000..f8f2d16
--- /dev/null
+++ b/docs/rules/0136/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 136
+permalink: /136/
+redirect_from:
+  - /0136/
+---
+
+# Custom methods
+
+{% include linter-aip-listing.md aip=136 %}
diff --git a/docs/rules/core/0136-prepositions.md b/docs/rules/0136/prepositions.md
similarity index 97%
rename from docs/rules/core/0136-prepositions.md
rename to docs/rules/0136/prepositions.md
index 4027861..1a56d02 100644
--- a/docs/rules/core/0136-prepositions.md
+++ b/docs/rules/0136/prepositions.md
@@ -3,6 +3,9 @@ rule:
   aip: 136
   name: [core, '0136', prepositions]
   summary: Custom methods must not include prepositions in their names.
+permalink: /136/prepositions
+redirect_from:
+  - /0136/prepositions
 ---
 
 # Custom methods: Prepositions
diff --git a/docs/rules/core/0136-verb-noun.md b/docs/rules/0136/verb-noun.md
similarity index 96%
rename from docs/rules/core/0136-verb-noun.md
rename to docs/rules/0136/verb-noun.md
index fd1b460..36dab8a 100644
--- a/docs/rules/core/0136-verb-noun.md
+++ b/docs/rules/0136/verb-noun.md
@@ -3,6 +3,9 @@ rule:
   aip: 136
   name: [core, '0136', verb-noun]
   summary: Custom methods should be named with the verb, then the noun.
+permalink: /136/verb-noun
+redirect_from:
+  - /0136/verb-noun
 ---
 
 # Custom methods: Verb and noun
diff --git a/docs/rules/core/0140-abbreviations.md b/docs/rules/0140/abbreviations.md
similarity index 95%
rename from docs/rules/core/0140-abbreviations.md
rename to docs/rules/0140/abbreviations.md
index 756deb7..d124f35 100644
--- a/docs/rules/core/0140-abbreviations.md
+++ b/docs/rules/0140/abbreviations.md
@@ -3,6 +3,9 @@ rule:
   aip: 140
   name: [core, '0140', abbreviations]
   summary: Field names should use common abbreviations.
+permalink: /140/abbreviations
+redirect_from:
+  - /0140/abbreviations
 ---
 
 # Field names: Abbreviations
diff --git a/docs/rules/core/0140-base64.md b/docs/rules/0140/base64.md
similarity index 96%
rename from docs/rules/core/0140-base64.md
rename to docs/rules/0140/base64.md
index 89ea382..5566e2c 100644
--- a/docs/rules/core/0140-base64.md
+++ b/docs/rules/0140/base64.md
@@ -3,6 +3,9 @@ rule:
   aip: 140
   name: [core, '0140', base64]
   summary: Base64 fields should use the `bytes` type.
+permalink: /140/base64
+redirect_from:
+  - /0140/base64
 ---
 
 # Base64 fields
diff --git a/docs/rules/0140/index.md b/docs/rules/0140/index.md
new file mode 100644
index 0000000..6b4186a
--- /dev/null
+++ b/docs/rules/0140/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 140
+permalink: /140/
+redirect_from:
+  - /0140/
+---
+
+# Field names
+
+{% include linter-aip-listing.md aip=140 %}
diff --git a/docs/rules/core/0140-lower-snake.md b/docs/rules/0140/lower-snake.md
similarity index 94%
rename from docs/rules/core/0140-lower-snake.md
rename to docs/rules/0140/lower-snake.md
index 15a3f05..44b35cf 100644
--- a/docs/rules/core/0140-lower-snake.md
+++ b/docs/rules/0140/lower-snake.md
@@ -3,6 +3,9 @@ rule:
   aip: 140
   name: [core, '0140', lower-snake]
   summary: Field names should use `snake_case`.
+permalink: /140/lower-snake
+redirect_from:
+  - /0140/lower-snake
 ---
 
 # Field names: Abbreviations
diff --git a/docs/rules/core/0140-prepositions.md b/docs/rules/0140/prepositions.md
similarity index 94%
rename from docs/rules/core/0140-prepositions.md
rename to docs/rules/0140/prepositions.md
index 7d919dc..a590fe5 100644
--- a/docs/rules/core/0140-prepositions.md
+++ b/docs/rules/0140/prepositions.md
@@ -3,6 +3,9 @@ rule:
   aip: 140
   name: [core, '0140', prepositions]
   summary: Fields must not include prepositions in their names.
+permalink: /140/prepositions
+redirect_from:
+  - /0140/prepositions
 ---
 
 # Field names: Prepositions
diff --git a/docs/rules/core/0141-count-suffix.md b/docs/rules/0141/count-suffix.md
similarity index 94%
rename from docs/rules/core/0141-count-suffix.md
rename to docs/rules/0141/count-suffix.md
index 079d21c..2f326e4 100644
--- a/docs/rules/core/0141-count-suffix.md
+++ b/docs/rules/0141/count-suffix.md
@@ -3,6 +3,9 @@ rule:
   aip: 141
   name: [core, '0141', count-suffix]
   summary: Quantities should use a `_count` suffix.
+permalink: /141/count-suffix
+redirect_from:
+  - /0141/count-suffix
 ---
 
 # Count suffix
diff --git a/docs/rules/core/0141-forbidden-types.md b/docs/rules/0141/forbidden-types.md
similarity index 94%
rename from docs/rules/core/0141-forbidden-types.md
rename to docs/rules/0141/forbidden-types.md
index 16e5bae..eb1669a 100644
--- a/docs/rules/core/0141-forbidden-types.md
+++ b/docs/rules/0141/forbidden-types.md
@@ -3,6 +3,9 @@ rule:
   aip: 141
   name: [core, '0141', forbidden-types]
   summary: Fields should avoid unsigned integer types.
+permalink: /141/forbidden-types
+redirect_from:
+  - /0141/forbidden-types
 ---
 
 # Forbidden types
diff --git a/docs/rules/0141/index.md b/docs/rules/0141/index.md
new file mode 100644
index 0000000..efd7755
--- /dev/null
+++ b/docs/rules/0141/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 141
+permalink: /141/
+redirect_from:
+  - /0141/
+---
+
+# Quantities
+
+{% include linter-aip-listing.md aip=141 %}
diff --git a/docs/rules/0142/index.md b/docs/rules/0142/index.md
new file mode 100644
index 0000000..8c7351a
--- /dev/null
+++ b/docs/rules/0142/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 142
+permalink: /142/
+redirect_from:
+  - /0142/
+---
+
+# Time and duration
+
+{% include linter-aip-listing.md aip=142 %}
diff --git a/docs/rules/core/0142-time-field-names.md b/docs/rules/0142/time-field-names.md
similarity index 95%
rename from docs/rules/core/0142-time-field-names.md
rename to docs/rules/0142/time-field-names.md
index a811c16..2a34085 100644
--- a/docs/rules/core/0142-time-field-names.md
+++ b/docs/rules/0142/time-field-names.md
@@ -3,6 +3,9 @@ rule:
   aip: 142
   name: [core, '0142', time-field-names]
   summary: Timestamps should use `google.protobuf.Timestamp`.
+permalink: /142/time-field-names
+redirect_from:
+  - /0142/time-field-names
 ---
 
 # Timestamp field names
diff --git a/docs/rules/core/0142-time-field-type.md b/docs/rules/0142/time-field-type.md
similarity index 94%
rename from docs/rules/core/0142-time-field-type.md
rename to docs/rules/0142/time-field-type.md
index 5600e35..9bc7d2d 100644
--- a/docs/rules/core/0142-time-field-type.md
+++ b/docs/rules/0142/time-field-type.md
@@ -3,6 +3,9 @@ rule:
   aip: 142
   name: [core, '0142', time-field-type]
   summary: Timestamps should use `google.protobuf.Timestamp`.
+permalink: /142/time-field-type
+redirect_from:
+  - /0142/time-field-type
 ---
 
 # Timestamp field type
diff --git a/docs/rules/0143/index.md b/docs/rules/0143/index.md
new file mode 100644
index 0000000..6d9aa49
--- /dev/null
+++ b/docs/rules/0143/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 143
+permalink: /143/
+redirect_from:
+  - /0143/
+---
+
+# Standardized codes
+
+{% include linter-aip-listing.md aip=143 %}
diff --git a/docs/rules/core/0143-standardized-codes.md b/docs/rules/0143/standardized-codes.md
similarity index 95%
rename from docs/rules/core/0143-standardized-codes.md
rename to docs/rules/0143/standardized-codes.md
index f57ca6f..b956f6d 100644
--- a/docs/rules/core/0143-standardized-codes.md
+++ b/docs/rules/0143/standardized-codes.md
@@ -3,6 +3,9 @@ rule:
   aip: 143
   name: [core, '0143', standard-codes]
   summary: Fields representing concepts with standardized codes must use them.
+permalink: /143/standardized-codes
+redirect_from:
+  - /0143/standardized-codes
 ---
 
 # Standardized codes
diff --git a/docs/rules/core/0143-string-type.md b/docs/rules/0143/string-type.md
similarity index 96%
rename from docs/rules/core/0143-string-type.md
rename to docs/rules/0143/string-type.md
index 894bb78..bb1c6d0 100644
--- a/docs/rules/core/0143-string-type.md
+++ b/docs/rules/0143/string-type.md
@@ -3,6 +3,9 @@ rule:
   aip: 143
   name: [core, '0143', string-type]
   summary: Fields representing standardized codes must be strings.
+permalink: /143/string-type
+redirect_from:
+  - /0143/string-type
 ---
 
 # Standardized code strings
diff --git a/docs/rules/0151/index.md b/docs/rules/0151/index.md
new file mode 100644
index 0000000..0537d39
--- /dev/null
+++ b/docs/rules/0151/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 151
+permalink: /151/
+redirect_from:
+  - /0151/
+---
+
+# Long-running operations
+
+{% include linter-aip-listing.md aip=151 %}
diff --git a/docs/rules/core/0151-lro-metadata-type.md b/docs/rules/0151/lro-metadata-type.md
similarity index 97%
rename from docs/rules/core/0151-lro-metadata-type.md
rename to docs/rules/0151/lro-metadata-type.md
index 82c626c..94171a8 100644
--- a/docs/rules/core/0151-lro-metadata-type.md
+++ b/docs/rules/0151/lro-metadata-type.md
@@ -3,6 +3,9 @@ rule:
   aip: 151
   name: [core, '0151', lro-metadata-type]
   summary: LRO methods must have a metadata type.
+permalink: /151/lro-metadata-type
+redirect_from:
+  - /0151/lro-metadata-type
 ---
 
 # LRO metadata type
diff --git a/docs/rules/core/0151-lro-response-type.md b/docs/rules/0151/lro-response-type.md
similarity index 97%
rename from docs/rules/core/0151-lro-response-type.md
rename to docs/rules/0151/lro-response-type.md
index 29a9199..74170fe 100644
--- a/docs/rules/core/0151-lro-response-type.md
+++ b/docs/rules/0151/lro-response-type.md
@@ -3,6 +3,9 @@ rule:
   aip: 151
   name: [core, '0151', lro-response-type]
   summary: LRO methods must have a response type.
+permalink: /151/lro-response-type
+redirect_from:
+  - /0151/lro-response-type
 ---
 
 # LRO response type
diff --git a/docs/rules/core/0151-lro-types-defined-in-file.md b/docs/rules/0151/lro-types-defined-in-file.md
similarity index 96%
rename from docs/rules/core/0151-lro-types-defined-in-file.md
rename to docs/rules/0151/lro-types-defined-in-file.md
index bba974e..b85dc9b 100644
--- a/docs/rules/core/0151-lro-types-defined-in-file.md
+++ b/docs/rules/0151/lro-types-defined-in-file.md
@@ -3,6 +3,9 @@ rule:
   aip: 151
   name: [core, '0151', lro-types-defined-in-file]
   summary: LRO response and metadata messages must be defined in the same file.
+permalink: /151/lro-types-defined-in-file
+redirect_from:
+  - /0151/lro-types-defined-in-file
 ---
 
 # LRO types defined in file
diff --git a/docs/rules/core/0151-operation-info.md b/docs/rules/0151/operation-info.md
similarity index 96%
rename from docs/rules/core/0151-operation-info.md
rename to docs/rules/0151/operation-info.md
index 5c89026..6d58c7c 100644
--- a/docs/rules/core/0151-operation-info.md
+++ b/docs/rules/0151/operation-info.md
@@ -3,6 +3,9 @@ rule:
   aip: 151
   name: [core, '0151', operation-info]
   summary: LRO methods must include an `operation_info` annotation.
+permalink: /151/operation-info
+redirect_from:
+  - /0151/operation-info
 ---
 
 # Long-running operation info
diff --git a/docs/rules/0158/index.md b/docs/rules/0158/index.md
new file mode 100644
index 0000000..f355c52
--- /dev/null
+++ b/docs/rules/0158/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 158
+permalink: /158/
+redirect_from:
+  - /0158/
+---
+
+# Pagination
+
+{% include linter-aip-listing.md aip=158 %}
diff --git a/docs/rules/core/0158-request-page-size-field.md b/docs/rules/0158/request-page-size-field.md
similarity index 95%
rename from docs/rules/core/0158-request-page-size-field.md
rename to docs/rules/0158/request-page-size-field.md
index f21c230..23d7084 100644
--- a/docs/rules/core/0158-request-page-size-field.md
+++ b/docs/rules/0158/request-page-size-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 158
   name: [core, '0158', request-page-size-field]
   summary: Paginated RPCs must have a `page_size` field in the request.
+permalink: /158/request-page-size-field
+redirect_from:
+  - /0158/request-page-size-field
 ---
 
 # Paginated methods: Page size field
diff --git a/docs/rules/core/0158-request-page-token-field.md b/docs/rules/0158/request-page-token-field.md
similarity index 94%
rename from docs/rules/core/0158-request-page-token-field.md
rename to docs/rules/0158/request-page-token-field.md
index fcbb763..e1e1e30 100644
--- a/docs/rules/core/0158-request-page-token-field.md
+++ b/docs/rules/0158/request-page-token-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 158
   name: [core, '0158', request-page-token-field]
   summary: Paginated RPCs must have a `page_token` field in the request.
+permalink: /158/request-page-token-field
+redirect_from:
+  - /0158/request-page-token-field
 ---
 
 # Paginated methods: Page token field
diff --git a/docs/rules/core/0158-response-next-page-token-field.md b/docs/rules/0158/response-next-page-token-field.md
similarity index 94%
rename from docs/rules/core/0158-response-next-page-token-field.md
rename to docs/rules/0158/response-next-page-token-field.md
index 669b9ab..a381725 100644
--- a/docs/rules/core/0158-response-next-page-token-field.md
+++ b/docs/rules/0158/response-next-page-token-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 158
   name: [core, '0158', response-next-page-token-field]
   summary: Paginated RPCs must have a `next_page_token` field in the response.
+permalink: /158/response-next-page-token-field
+redirect_from:
+  - /0158/response-next-page-token-field
 ---
 
 # Paginated methods: Next page token field
diff --git a/docs/rules/core/0191-filenames.md b/docs/rules/0191/filenames.md
similarity index 94%
rename from docs/rules/core/0191-filenames.md
rename to docs/rules/0191/filenames.md
index b0bda45..564660b 100644
--- a/docs/rules/core/0191-filenames.md
+++ b/docs/rules/0191/filenames.md
@@ -3,6 +3,9 @@ rule:
   aip: 191
   name: [core, '0191', filenames]
   summary: Proto files must use reasonable filenames.
+permalink: /191/filenames
+redirect_from:
+  - /0191/filenames
 ---
 
 # Proto3 syntax
diff --git a/docs/rules/0191/index.md b/docs/rules/0191/index.md
new file mode 100644
index 0000000..4b4e71d
--- /dev/null
+++ b/docs/rules/0191/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 191
+permalink: /191/
+redirect_from:
+  - /0191/
+---
+
+# File and directory structure
+
+{% include linter-aip-listing.md aip=191 %}
diff --git a/docs/rules/core/0191-proto-version.md b/docs/rules/0191/proto-version.md
similarity index 93%
rename from docs/rules/core/0191-proto-version.md
rename to docs/rules/0191/proto-version.md
index 6bfb0f5..0206b7a 100644
--- a/docs/rules/core/0191-proto-version.md
+++ b/docs/rules/0191/proto-version.md
@@ -3,6 +3,9 @@ rule:
   aip: 191
   name: [core, '0191', proto-version]
   summary: All proto files must use proto3.
+permalink: /191/proto-version
+redirect_from:
+  - /0191/proto-version
 ---
 
 # Proto3 syntax
diff --git a/docs/rules/core/0192-has-comments.md b/docs/rules/0192/has-comments.md
similarity index 94%
rename from docs/rules/core/0192-has-comments.md
rename to docs/rules/0192/has-comments.md
index 5759ec9..a874553 100644
--- a/docs/rules/core/0192-has-comments.md
+++ b/docs/rules/0192/has-comments.md
@@ -3,6 +3,9 @@ rule:
   aip: 192
   name: [core, '0192', has-comments]
   summary: Everything in a proto file must be commented.
+permalink: /192/has-comments
+redirect_from:
+  - /0192/has-comments
 ---
 
 # Proto3 syntax
diff --git a/docs/rules/0192/index.md b/docs/rules/0192/index.md
new file mode 100644
index 0000000..39bb747
--- /dev/null
+++ b/docs/rules/0192/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 192
+permalink: /192/
+redirect_from:
+  - /0192/
+---
+
+# Documentation
+
+{% include linter-aip-listing.md aip=192 %}
diff --git a/docs/rules/core/0192-only-leading-comments.md b/docs/rules/0192/only-leading-comments.md
similarity index 94%
rename from docs/rules/core/0192-only-leading-comments.md
rename to docs/rules/0192/only-leading-comments.md
index 7173452..024b991 100644
--- a/docs/rules/core/0192-only-leading-comments.md
+++ b/docs/rules/0192/only-leading-comments.md
@@ -3,6 +3,9 @@ rule:
   aip: 192
   name: [core, '0192', only-leading-comments]
   summary: All public comments should be leading comments.
+permalink: /192/only-leading-comments
+redirect_from:
+  - /0192/only-leading-comments
 ---
 
 # Proto3 syntax
diff --git a/docs/rules/core/0203-immutable.md b/docs/rules/0203/immutable.md
similarity index 96%
rename from docs/rules/core/0203-immutable.md
rename to docs/rules/0203/immutable.md
index 3758c11..b468eec 100644
--- a/docs/rules/core/0203-immutable.md
+++ b/docs/rules/0203/immutable.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', immutable]
   summary: Immutable fields should be annotated.
+permalink: /203/immutable
+redirect_from:
+  - /0203/immutable
 ---
 
 # Immutable fields
diff --git a/docs/rules/0203/index.md b/docs/rules/0203/index.md
new file mode 100644
index 0000000..fc78726
--- /dev/null
+++ b/docs/rules/0203/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 203
+permalink: /203/
+redirect_from:
+  - /0203/
+---
+
+# Field behavior
+
+{% include linter-aip-listing.md aip=203 %}
diff --git a/docs/rules/core/0203-input-only.md b/docs/rules/0203/input-only.md
similarity index 96%
rename from docs/rules/core/0203-input-only.md
rename to docs/rules/0203/input-only.md
index 81d2f84..5f9f035 100644
--- a/docs/rules/core/0203-input-only.md
+++ b/docs/rules/0203/input-only.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', input-only]
   summary: Input only fields should be annotated.
+permalink: /203/input-only
+redirect_from:
+  - /0203/input-only
 ---
 
 # Input only fields
diff --git a/docs/rules/core/0203-optional-conflict.md b/docs/rules/0203/optional-conflict.md
similarity index 95%
rename from docs/rules/core/0203-optional-conflict.md
rename to docs/rules/0203/optional-conflict.md
index b23c780..f399c4c 100644
--- a/docs/rules/core/0203-optional-conflict.md
+++ b/docs/rules/0203/optional-conflict.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', optional-conflict]
   summary: Optional fields must not have any other field behavior.
+permalink: /203/optional-conflict
+redirect_from:
+  - /0203/optional-conflict
 ---
 
 # Optional fields: Conflicts
diff --git a/docs/rules/core/0203-optional-consistency.md b/docs/rules/0203/optional-consistency.md
similarity index 96%
rename from docs/rules/core/0203-optional-consistency.md
rename to docs/rules/0203/optional-consistency.md
index 032c74f..106ea68 100644
--- a/docs/rules/core/0203-optional-consistency.md
+++ b/docs/rules/0203/optional-consistency.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', optional-consistency]
   summary: Optional fields should either be always or never annotated.
+permalink: /203/optional-consistency
+redirect_from:
+  - /0203/optional-consistency
 ---
 
 # Optional fields: Consistency
diff --git a/docs/rules/core/0203-optional.md b/docs/rules/0203/optional.md
similarity index 96%
rename from docs/rules/core/0203-optional.md
rename to docs/rules/0203/optional.md
index d1cbd86..3988625 100644
--- a/docs/rules/core/0203-optional.md
+++ b/docs/rules/0203/optional.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', optional]
   summary: Optional fields may be annotated.
+permalink: /203/optional
+redirect_from:
+  - /0203/optional
 ---
 
 # Optional fields
diff --git a/docs/rules/core/0203-output-only.md b/docs/rules/0203/output-only.md
similarity index 96%
rename from docs/rules/core/0203-output-only.md
rename to docs/rules/0203/output-only.md
index 1e73a20..0217816 100644
--- a/docs/rules/core/0203-output-only.md
+++ b/docs/rules/0203/output-only.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', output-only]
   summary: Output only fields should be annotated.
+permalink: /203/output-only
+redirect_from:
+  - /0203/output-only
 ---
 
 # Output only fields
diff --git a/docs/rules/core/0203-required.md b/docs/rules/0203/required.md
similarity index 96%
rename from docs/rules/core/0203-required.md
rename to docs/rules/0203/required.md
index 64c7a3e..b614981 100644
--- a/docs/rules/core/0203-required.md
+++ b/docs/rules/0203/required.md
@@ -3,6 +3,9 @@ rule:
   aip: 203
   name: [core, '0203', required]
   summary: Required fields should be annotated.
+permalink: /203/required
+redirect_from:
+  - /0203/required
 ---
 
 # Required fields
diff --git a/docs/rules/0216/index.md b/docs/rules/0216/index.md
new file mode 100644
index 0000000..4a76898
--- /dev/null
+++ b/docs/rules/0216/index.md
@@ -0,0 +1,10 @@
+---
+aip_listing: 216
+permalink: /216/
+redirect_from:
+  - /0216/
+---
+
+# States
+
+{% include linter-aip-listing.md aip=216 %}
diff --git a/docs/rules/core/0216-synonyms.md b/docs/rules/0216/synonyms.md
similarity index 95%
rename from docs/rules/core/0216-synonyms.md
rename to docs/rules/0216/synonyms.md
index d18f656..152185e 100644
--- a/docs/rules/core/0216-synonyms.md
+++ b/docs/rules/0216/synonyms.md
@@ -3,6 +3,9 @@ rule:
   aip: 216
   name: [core, '0216', synonyms]
   summary: Lifecycle state enums should be called "State", not "Status".
+permalink: /216/synonyms
+redirect_from:
+  - /0216/synonyms
 ---
 
 # States
diff --git a/docs/rules/core/0231-http-body.md b/docs/rules/0231/http-body.md
similarity index 96%
rename from docs/rules/core/0231-http-body.md
rename to docs/rules/0231/http-body.md
index 73ff6e1..3c345ff 100644
--- a/docs/rules/core/0231-http-body.md
+++ b/docs/rules/0231/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', http-body]
   summary: Batch Get methods must not have an HTTP body.
+permalink: /231/http-body
+redirect_from:
+  - /0231/http-body
 ---
 
 # Batch Get methods: No HTTP body
diff --git a/docs/rules/core/0231-http-method.md b/docs/rules/0231/http-method.md
similarity index 96%
rename from docs/rules/core/0231-http-method.md
rename to docs/rules/0231/http-method.md
index d7162d0..a93bf8d 100644
--- a/docs/rules/core/0231-http-method.md
+++ b/docs/rules/0231/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', http-method]
   summary: Batch Get methods must use the GET HTTP verb.
+permalink: /231/http-method
+redirect_from:
+  - /0231/http-method
 ---
 
 # Get methods: GET HTTP verb
diff --git a/docs/rules/core/0231-http-uri-suffix.md b/docs/rules/0231/http-uri-suffix.md
similarity index 95%
rename from docs/rules/core/0231-http-uri-suffix.md
rename to docs/rules/0231/http-uri-suffix.md
index 7fc771e..285d463 100644
--- a/docs/rules/core/0231-http-uri-suffix.md
+++ b/docs/rules/0231/http-uri-suffix.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', http-uri-suffix]
   summary: Batch Get methods must have the correct URI suffix
+permalink: /231/http-uri-suffix
+redirect_from:
+  - /0231/http-uri-suffix
 ---
 
 # Batch Get methods: URI suffix
diff --git a/docs/rules/0231/index.md b/docs/rules/0231/index.md
new file mode 100644
index 0000000..0eb6fde
--- /dev/null
+++ b/docs/rules/0231/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 231
+permalink: /231/
+redirect_from:
+  - /0231/
+prose_title: batch get methods
+---
+
+# Batch methods: Get
+
+{% include linter-aip-listing.md aip=231 %}
diff --git a/docs/rules/core/0231-plural-method-name.md b/docs/rules/0231/plural-method-name.md
similarity index 95%
rename from docs/rules/core/0231-plural-method-name.md
rename to docs/rules/0231/plural-method-name.md
index 4d8d0bb..e5d60c8 100644
--- a/docs/rules/core/0231-plural-method-name.md
+++ b/docs/rules/0231/plural-method-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', plural-method-name]
   summary: Batch Get methods must have plural names.
+permalink: /231/plural-method-name
+redirect_from:
+  - /0231/plural-method-name
 ---
 
 # Batch Get methods: Plural method name
diff --git a/docs/rules/core/0231-request-message-name.md b/docs/rules/0231/request-message-name.md
similarity index 95%
rename from docs/rules/core/0231-request-message-name.md
rename to docs/rules/0231/request-message-name.md
index 21633de..a8a7926 100644
--- a/docs/rules/core/0231-request-message-name.md
+++ b/docs/rules/0231/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', request-message-name]
   summary: Batch Get methods must have standardized request message names.
+permalink: /231/request-message-name
+redirect_from:
+  - /0231/request-message-name
 ---
 
 # Batch Get methods: Request message
diff --git a/docs/rules/core/0231-request-names-field.md b/docs/rules/0231/request-names-field.md
similarity index 95%
rename from docs/rules/core/0231-request-names-field.md
rename to docs/rules/0231/request-names-field.md
index 374570e..54912a3 100644
--- a/docs/rules/core/0231-request-names-field.md
+++ b/docs/rules/0231/request-names-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', request-names-field]
   summary: Batch Get RPCs should have a `names` field in the request.
+permalink: /231/request-names-field
+redirect_from:
+  - /0231/request-names-field
 ---
 
 # Batch Get methods: Names field
diff --git a/docs/rules/core/0231-request-parent-field.md b/docs/rules/0231/request-parent-field.md
similarity index 95%
rename from docs/rules/core/0231-request-parent-field.md
rename to docs/rules/0231/request-parent-field.md
index e91b0c0..4aeeb0e 100644
--- a/docs/rules/core/0231-request-parent-field.md
+++ b/docs/rules/0231/request-parent-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', request-parent-field]
   summary: Batch Get RPCs must have a `parent` field in the request.
+permalink: /231/request-parent-field
+redirect_from:
+  - /0231/request-parent-field
 ---
 
 # Batch Get methods: Parent field
diff --git a/docs/rules/core/0231-response-message-name.md b/docs/rules/0231/response-message-name.md
similarity index 95%
rename from docs/rules/core/0231-response-message-name.md
rename to docs/rules/0231/response-message-name.md
index 860f576..36023ac 100644
--- a/docs/rules/core/0231-response-message-name.md
+++ b/docs/rules/0231/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 231
   name: [core, '0231', response-message-name]
   summary: Batch Get methods must have standardized response message names.
+permalink: /231/response-message-name
+redirect_from:
+  - /0231/response-message-name
 ---
 
 # Batch Get methods: Response message
diff --git a/docs/rules/core/0231-response-resource-field.md b/docs/rules/0231/response-resource-field.md
similarity index 94%
rename from docs/rules/core/0231-response-resource-field.md
rename to docs/rules/0231/response-resource-field.md
index 5b31f99..e436152 100644
--- a/docs/rules/core/0231-response-resource-field.md
+++ b/docs/rules/0231/response-resource-field.md
@@ -4,6 +4,9 @@ rule:
   name: [core, '0231', response-resource-field]
   summary:
     Batch Get RPCs must have a repeated field for the resource in the response.
+permalink: /231/response-resource-field
+redirect_from:
+  - /0231/response-resource-field
 ---
 
 # Batch Get methods: Resource field
diff --git a/docs/rules/core/0233-http-body.md b/docs/rules/0233/http-body.md
similarity index 96%
rename from docs/rules/core/0233-http-body.md
rename to docs/rules/0233/http-body.md
index 14f83e9..f30e921 100644
--- a/docs/rules/core/0233-http-body.md
+++ b/docs/rules/0233/http-body.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', http-body]
   summary: Batch Create methods should use `*` as the HTTP body.
+permalink: /233/http-body
+redirect_from:
+  - /0233/http-body
 ---
 
 # Batch Create methods: HTTP body 
diff --git a/docs/rules/core/0233-http-method.md b/docs/rules/0233/http-method.md
similarity index 96%
rename from docs/rules/core/0233-http-method.md
rename to docs/rules/0233/http-method.md
index 5cf3b50..c93d0ed 100644
--- a/docs/rules/core/0233-http-method.md
+++ b/docs/rules/0233/http-method.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', http-method]
   summary: Batch Create methods must use the POST HTTP verb.
+permalink: /233/http-method
+redirect_from:
+  - /0233/http-method
 ---
 
 # Batch Create methods: POST HTTP verb
diff --git a/docs/rules/core/0233-http-uri-suffix.md b/docs/rules/0233/http-uri-suffix.md
similarity index 95%
rename from docs/rules/core/0233-http-uri-suffix.md
rename to docs/rules/0233/http-uri-suffix.md
index eb57451..f1e12c1 100644
--- a/docs/rules/core/0233-http-uri-suffix.md
+++ b/docs/rules/0233/http-uri-suffix.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', http-uri-suffix]
   summary: Batch Create methods must have the correct URI suffix
+permalink: /233/http-uri-suffix
+redirect_from:
+  - /0233/http-uri-suffix
 ---
 
 # Batch Create methods: URI suffix
diff --git a/docs/rules/0233/index.md b/docs/rules/0233/index.md
new file mode 100644
index 0000000..0aac7f4
--- /dev/null
+++ b/docs/rules/0233/index.md
@@ -0,0 +1,11 @@
+---
+aip_listing: 233
+permalink: /233/
+redirect_from:
+  - /0233/
+prose_title: batch create methods
+---
+
+# Batch methods: Create
+
+{% include linter-aip-listing.md aip=233 %}
diff --git a/docs/rules/core/0233-plural-method-name.md b/docs/rules/0233/plural-method-name.md
similarity index 95%
rename from docs/rules/core/0233-plural-method-name.md
rename to docs/rules/0233/plural-method-name.md
index e1fdd75..e548570 100644
--- a/docs/rules/core/0233-plural-method-name.md
+++ b/docs/rules/0233/plural-method-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', plural-method-name]
   summary: Batch Create methods must have plural names.
+permalink: /233/plural-method-name
+redirect_from:
+  - /0233/plural-method-name
 ---
 
 # Batch Create methods: Plural method name
diff --git a/docs/rules/core/0233-request-message-name.md b/docs/rules/0233/request-message-name.md
similarity index 95%
rename from docs/rules/core/0233-request-message-name.md
rename to docs/rules/0233/request-message-name.md
index 8dd3772..b5aa784 100644
--- a/docs/rules/core/0233-request-message-name.md
+++ b/docs/rules/0233/request-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', request-message-name]
   summary: Batch Create methods must have standardized request message names.
+permalink: /233/request-message-name
+redirect_from:
+  - /0233/request-message-name
 ---
 
 # Batch Create methods: Request message
diff --git a/docs/rules/core/0233-request-parent-field.md b/docs/rules/0233/request-parent-field.md
similarity index 95%
rename from docs/rules/core/0233-request-parent-field.md
rename to docs/rules/0233/request-parent-field.md
index afa4c26..c3fb695 100644
--- a/docs/rules/core/0233-request-parent-field.md
+++ b/docs/rules/0233/request-parent-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', request-parent-field]
   summary: Batch Create RPCs must have a `parent` field in the request.
+permalink: /233/request-parent-field
+redirect_from:
+  - /0233/request-parent-field
 ---
 
 # Batch Create methods: Parent field
diff --git a/docs/rules/core/0233-request-requests-field.md b/docs/rules/0233/request-requests-field.md
similarity index 95%
rename from docs/rules/core/0233-request-requests-field.md
rename to docs/rules/0233/request-requests-field.md
index 37e3d1a..14ed503 100644
--- a/docs/rules/core/0233-request-requests-field.md
+++ b/docs/rules/0233/request-requests-field.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', request-requests-field]
   summary: Batch Create RPCs should have a `requests` field in the request.
+permalink: /233/request-requests-field
+redirect_from:
+  - /0233/request-requests-field
 ---
 
 # Batch Create methods: Requests field
diff --git a/docs/rules/core/0233-response-message-name.md b/docs/rules/0233/response-message-name.md
similarity index 96%
rename from docs/rules/core/0233-response-message-name.md
rename to docs/rules/0233/response-message-name.md
index 32de90d..36e8dae 100644
--- a/docs/rules/core/0233-response-message-name.md
+++ b/docs/rules/0233/response-message-name.md
@@ -3,6 +3,9 @@ rule:
   aip: 233
   name: [core, '0233', response-message-name]
   summary: Batch Create methods must have standardized response message names.
+permalink: /233/response-message-name
+redirect_from:
+  - /0233/response-message-name
 ---
 
 # Batch Create methods: Response message
diff --git a/docs/rules/core/0233-response-resource-field.md b/docs/rules/0233/response-resource-field.md
similarity index 94%
rename from docs/rules/core/0233-response-resource-field.md
rename to docs/rules/0233/response-resource-field.md
index 1bd34cd..24d7965 100644
--- a/docs/rules/core/0233-response-resource-field.md
+++ b/docs/rules/0233/response-resource-field.md
@@ -4,6 +4,9 @@ rule:
   name: [core, '0233', response-resource-field]
   summary:
     Batch Create RPCs must have a repeated field for the resource in the response.
+permalink: /233/response-resource-field
+redirect_from:
+  - /0233/response-resource-field
 ---
 
 # Batch Create methods: Resource field
diff --git a/docs/rules/core/index.md b/docs/rules/core.md
similarity index 82%
rename from docs/rules/core/index.md
rename to docs/rules/core.md
index f3b5947..a0a79d3 100644
--- a/docs/rules/core/index.md
+++ b/docs/rules/core.md
@@ -1,4 +1,5 @@
 ---
+permalink: /rules/core/
 ---
 
 # Core rules
@@ -8,6 +9,6 @@ default, and **should** be enabled for most APIs. It is recommended that APIs
 which do not want to follow certain AIPs within the core section disable those
 rules individually, rather than disabling the core rules as a set.
 
-{% include linter-rule-listing.html start=100 end=999 %}
+{% include linter-group-listing.html start=100 end=999 %}
 
 [generally-applicable aips]: https://aip.dev/general
diff --git a/lint/problem.go b/lint/problem.go
index 9ef5cbd..439801e 100644
--- a/lint/problem.go
+++ b/lint/problem.go
@@ -162,9 +162,8 @@ func fileLocationFromPBLocation(l *dpb.SourceCodeInfo_Location) fileLocation {
 
 // ruleDocURI returns the link to the rule doc in https://linter.aip.dev/.
 func ruleDocURI(name RuleName) string {
-	base := "https://linter.aip.dev/rules"
+	base := "https://linter.aip.dev/"
 	nameParts := strings.Split(string(name), "::") // e.g., core::0122::camel-case-uri -> ["core", "0122", "camel-case-uri"]
-	ruleSet := nameParts[0]
-	path := strings.Join(nameParts[1:], "-")
-	return base + "/" + ruleSet + "/" + path + ".html"
+	path := strings.TrimPrefix(strings.Join(nameParts[1:], "/"), "0")
+	return base + path
 }


Test patch:
diff --git a/lint/problem_test.go b/lint/problem_test.go
index a99b000..3d933e8 100644
--- a/lint/problem_test.go
+++ b/lint/problem_test.go
@@ -126,7 +126,7 @@ func TestRuleDocURI(t *testing.T) {
 	}{{
 		name:     "CoreRule",
 		ruleName: NewRuleName(122, "camel-case-uris"),
-		wantURI:  "https://linter.aip.dev/rules/core/0122-camel-case-uris.html",
+		wantURI:  "https://linter.aip.dev/122/camel-case-uris",
 	}}
 
 	for _, test := range cases {