Skip to content

Fix module section heading collisions#73

Closed
flybayer wants to merge 1 commit into
mainfrom
bb/fix-mintlify-module-search
Closed

Fix module section heading collisions#73
flybayer wants to merge 1 commit into
mainfrom
bb/fix-mintlify-module-search

Conversation

@flybayer

@flybayer flybayer commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

  • rename module section labels that collide with their page title anchors
  • publish patch versions for the four affected standard-library modules
  • reject future section labels that generate the same anchor as a definition name

Why

Mintlify appends a slugified page title to page-level search links. When a later section has the same slug, selecting the page result jumps to that section instead of the page top.

Verification

  • npm run typecheck
  • npm test (74 tests)
  • compiled all 19 definitions with compileAllDefinitions()

Greptile Summary

This PR fixes a Mintlify navigation bug where section headings whose slugified labels matched the slugified page title caused search results to jump to the section rather than the page top. It renames the colliding section labels in four definition files and adds a compile-time guardrail to prevent the pattern from reoccurring.

  • Four rvn-*-definition.yml files rename their primary section label by appending " config" (e.g. "EC2 service" → "EC2 service config"), each receiving a patch version bump. All new labels follow the sentence-case rule in AGENTS.md.
  • guardrails.ts adds validateSectionLabelsDoNotCollideWithDefinitionNames, which slugifies each section label and compares it against the definition's own name slug; compiler.ts wires it into compileAllDefinitions.
  • A new test in guardrails.test.ts writes a real on-disk fixture and asserts the guardrail throws the expected GuardrailError.

Confidence Score: 4/5

Safe to merge. The YAML renames and guardrail logic are correct for all existing module definitions and the new test validates the failure path end-to-end.

The slugifyHeading helper approximates Mintlify's anchor algorithm but strips characters (&, /, .) that Mintlify actually preserves or converts differently. This won't affect any current module labels, which are all plain English phrases, but could silently miss a real collision if a future label or definition name contains those characters.

Files Needing Attention: tools/ravion-modules/src/guardrails.ts — the slugifyHeading regex is worth tightening to match Mintlify's documented algorithm before new modules with special characters in their names are added.

Important Files Changed

Filename Overview
tools/ravion-modules/src/guardrails.ts Adds validateSectionLabelsDoNotCollideWithDefinitionNames and slugifyHeading; the slug regex is an approximation of Mintlify's algorithm that drops characters (&, /, .) which Mintlify actually handles differently, creating a potential false-negative gap for future module labels.
tools/ravion-modules/src/compiler.ts Wires the new section-label collision guardrail into compileAllDefinitions; minimal change, looks correct.
tools/ravion-modules/test/guardrails.test.ts Adds a test that writes a real on-disk YAML fixture with a colliding section label and asserts compileAllDefinitions throws a GuardrailError; well-structured and covers the failure path.
compute/ec2_service/rvn-ec2-service-definition.yml Section label renamed from 'EC2 service' to 'EC2 service config' and version bumped to 0.1.2 (patch); fix is correct and label uses sentence case per AGENTS.md.
compute/ecs_cluster/rvn-ecs-cluster-definition.yml Section label renamed from 'ECS cluster' to 'ECS cluster config' and version bumped to 0.3.2; correct patch-level change.
compute/lambda/rvn-lambda-definition.yml Section label renamed from 'Lambda function' to 'Lambda function config' and version bumped to 0.3.3; correct patch-level change.
hosting/static_site/rvn-aws-static-definition.yml Section label renamed from 'Static hosting' to 'Static hosting config' and version bumped to 0.3.5; correct patch-level change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["compileAllDefinitions()"] --> B["compile each rvn-*-definition.yml"]
    B --> C["validateUniqueDefinitionTypes()"]
    C --> D["validateSectionLabelsDoNotCollideWithDefinitionNames()"]
    D --> E{For each definition}
    E --> F["slugifyHeading(definition.name)"]
    E --> G["For each section input:\nslugifyHeading(input.label)"]
    F --> H{slugs equal?}
    G --> H
    H -- Yes --> I["Collect collision"]
    H -- No --> E
    I --> J["throw GuardrailError"]
    D --> K["Return compiled definitions"]
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
tools/ravion-modules/src/guardrails.ts:175-181
**`slugifyHeading` diverges from Mintlify's actual anchor algorithm**

According to Mintlify's documentation, their algorithm preserves `&` and `/`, converts `.` to `-`, and turns straight apostrophes into right single quotation marks. The current regex `.replace(/[^a-z0-9_\s-]/g, "")` strips all of these, so a section labeled "Read/write config" would produce `readwrite-config` here but `read/write-config` in Mintlify — a real collision against a definition named "Read/Write Config" would pass the guardrail undetected. All current labels are simple English phrases so there is no present defect, but the check could silently miss future collisions that involve `&`, `/`, `.`, or `'`.

Reviews (1): Last reviewed commit: "fix module section heading collisions" | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@github-actions

Copy link
Copy Markdown

Ravion Module Publish Plan

Dry run only. No Ravion API mutations were made.

Module Current Version New Version Description
rvn-aws-static 0.3.4 0.3.5 Clarify the static hosting configuration section label.
rvn-ec2-service 0.1.1 0.1.2 Clarify the EC2 service configuration section label.
rvn-ecs-cluster 0.3.1 0.3.2 Clarify the ECS cluster configuration section label.
rvn-lambda 0.3.2 0.3.3 Clarify the Lambda function configuration section label.

Diffs

rvn-aws-static 0.3.4 -> 0.3.5

--- remote
+++ compiled
     type: string
     values: $values:aws/regions
   - id: section_hosting
-    label: Static hosting
+    label: Static hosting config
     type: section
   - default: <<project.given_id>>-<<environment.given_id>>-<<module.given_id>>
     description: Name for the bucket and other resources' prefix. Must be globally unique.
@@
 
   Every deployment is versioned. The deploy step promotes an S3 directory by updating the CloudFront KeyValueStore active pointer. CloudFront rewrites viewer requests to the active version prefix before it reads from S3.
 
-  Terraform source: [flightcontrolhq/modules/hosting/static_site](https://github.com/flightcontrolhq/modules/tree/rvn-aws-static@0.3.4/hosting/static_site)
+  Terraform source: [flightcontrolhq/modules/hosting/static_site](https://github.com/flightcontrolhq/modules/tree/rvn-aws-static@0.3.5/hosting/static_site)
 
   ## Use cases
 
@@
         base_path: hosting/static_site
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-aws-static@0.3.4
+        ref: rvn-aws-static@0.3.5
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

rvn-ec2-service 0.1.1 -> 0.1.2

--- remote
+++ compiled
     label: Run in private subnets
     type: boolean
   - id: section_service
-    label: EC2 service
+    label: EC2 service config
     type: section
   - default: <<project.given_id>>-<<environment.given_id>>-<<module.given_id>>
     description: Name for the instance group and related resources.
@@
 
   Ravion provisions the launch template, Auto Scaling Group, instance role and security group, SSM deploy document, app log group, and optional target group and listener rule. Web services can attach to a standalone Application Load Balancer or reuse the public or private ALB from an ECS cluster. The selected network and load balancer must use the same AWS account, region, and VPC. The Auto Scaling Group can still terminate instances because of scaling, health failures, or operator action. Root and data EBS volumes survive releases, but not instance termination.
 
-  Terraform source: [flightcontrolhq/modules/compute/ec2_service](https://github.com/flightcontrolhq/modules/tree/rvn-ec2-service@0.1.1/compute/ec2_service)
+  Terraform source: [flightcontrolhq/modules/compute/ec2_service](https://github.com/flightcontrolhq/modules/tree/rvn-ec2-service@0.1.2/compute/ec2_service)
 
   ## Use cases
 
@@
         base_path: compute/ec2_service
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-ec2-service@0.1.1
+        ref: rvn-ec2-service@0.1.2
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

rvn-ecs-cluster 0.3.1 -> 0.3.2

--- remote
+++ compiled
     required: true
     type: $ref:rvn-aws-network
   - id: section_cluster
-    label: ECS cluster
+    label: ECS cluster config
     type: section
   - default: <<project.given_id>>-<<environment.given_id>>
     description: Name prefix for all resources. Terraform requires 1-28 characters so generated ALB names fit AWS limits.
@@
   - **Public and private Network Load Balancers** for TCP/UDP and static IP use cases
   - **CloudWatch Container Insights** dashboard metrics for production visibility
 
-  Terraform source: [flightcontrolhq/modules/compute/ecs_cluster](https://github.com/flightcontrolhq/modules/tree/rvn-ecs-cluster@0.3.1/compute/ecs_cluster)
+  Terraform source: [flightcontrolhq/modules/compute/ecs_cluster](https://github.com/flightcontrolhq/modules/tree/rvn-ecs-cluster@0.3.2/compute/ecs_cluster)
 
   ## Use cases
 
@@
         base_path: compute/ecs_cluster
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-ecs-cluster@0.3.1
+        ref: rvn-ecs-cluster@0.3.2
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

rvn-lambda 0.3.2 -> 0.3.3

--- remote
+++ compiled
     type: string
     values: $values:aws/regions
   - id: section_function
-    label: Lambda function
+    label: Lambda function config
     type: section
   - default: regional
     id: lambda_type
@@
 
   The Lambda Function module creates an AWS Lambda function, execution role, CloudWatch log group, optional artifact bucket, and a live alias that Ravion updates during deployments. Terraform provisions the long-lived function infrastructure with either a bootstrap zip package or a bootstrap container image. Deployments publish a new function version and move the live alias.
 
-  Terraform source: [flightcontrolhq/modules/compute/lambda](https://github.com/flightcontrolhq/modules/tree/rvn-lambda@0.3.2/compute/lambda)
+  Terraform source: [flightcontrolhq/modules/compute/lambda](https://github.com/flightcontrolhq/modules/tree/rvn-lambda@0.3.3/compute/lambda)
 
   ## Use cases
 
@@
         base_path: compute/lambda
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-lambda@0.3.2
+        ref: rvn-lambda@0.3.3
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

@flybayer

Copy link
Copy Markdown
Member Author

Closing this PR because section labels are product-facing module configuration. The search-anchor workaround belongs only in docs generation.

@flybayer flybayer closed this Jul 24, 2026
@flybayer
flybayer deleted the bb/fix-mintlify-module-search branch July 24, 2026 21:09
Comment on lines +175 to +181
function slugifyHeading(value: string): string {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9_\s-]/g, "")
.replace(/\s+/g, "-");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 partner slugifyHeading diverges from Mintlify's actual anchor algorithm

According to Mintlify's documentation, their algorithm preserves & and /, converts . to -, and turns straight apostrophes into right single quotation marks. The current regex .replace(/[^a-z0-9_\s-]/g, "") strips all of these, so a section labeled "Read/write config" would produce readwrite-config here but read/write-config in Mintlify — a real collision against a definition named "Read/Write Config" would pass the guardrail undetected. All current labels are simple English phrases so there is no present defect, but the check could silently miss future collisions that involve &, /, ., or '.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tools/ravion-modules/src/guardrails.ts
Line: 175-181

Comment:
**`slugifyHeading` diverges from Mintlify's actual anchor algorithm**

According to Mintlify's documentation, their algorithm preserves `&` and `/`, converts `.` to `-`, and turns straight apostrophes into right single quotation marks. The current regex `.replace(/[^a-z0-9_\s-]/g, "")` strips all of these, so a section labeled "Read/write config" would produce `readwrite-config` here but `read/write-config` in Mintlify — a real collision against a definition named "Read/Write Config" would pass the guardrail undetected. All current labels are simple English phrases so there is no present defect, but the check could silently miss future collisions that involve `&`, `/`, `.`, or `'`.

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant