[object Object]

← back to Cli Printing Press

fix(parser): check OpenAPI before GraphQL to prevent false positives

9c4349a3b4471a13e4527e646d1c3d77b87c98e6 · 2026-03-27 14:54:54 -0700 · Matt Van Horn

- Swap detection order: OpenAPI first, then GraphQL SDL
- Fixes false positive where "scalar" in OpenAPI descriptions triggered GraphQL parser
- Fix camelToKebab to handle acronyms (PR->pr, SQL->sql, not p-r-triage)
- Wire verify command into CLI root

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files touched

Diff

commit 9c4349a3b4471a13e4527e646d1c3d77b87c98e6
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date:   Fri Mar 27 14:54:54 2026 -0700

    fix(parser): check OpenAPI before GraphQL to prevent false positives
    
    - Swap detection order: OpenAPI first, then GraphQL SDL
    - Fixes false positive where "scalar" in OpenAPI descriptions triggered GraphQL parser
    - Fix camelToKebab to handle acronyms (PR->pr, SQL->sql, not p-r-triage)
    - Wire verify command into CLI root
    
    Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---
 internal/cli/root.go        |  7 ++++---
 internal/pipeline/verify.go | 19 +++++++++++++++++--
 2 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/internal/cli/root.go b/internal/cli/root.go
index 49abdd70..3f12aeb6 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -39,6 +39,7 @@ func Execute() error {
 	rootCmd.AddCommand(newGenerateCmd())
 	rootCmd.AddCommand(newScorecardCmd())
 	rootCmd.AddCommand(newDogfoodCmd())
+	rootCmd.AddCommand(newVerifyCmd())
 	rootCmd.AddCommand(newVisionCmd())
 	rootCmd.AddCommand(newVersionCmd())
 	rootCmd.AddCommand(newPrintCmd())
@@ -180,14 +181,14 @@ func newGenerateCmd() *cobra.Command {
 				}
 
 				var apiSpec *spec.APISpec
-				if graphql.IsGraphQLSDL(data) {
-					apiSpec, err = graphql.ParseSDLBytes(specFile, data)
-				} else if openapi.IsOpenAPI(data) {
+				if openapi.IsOpenAPI(data) {
 					if lenient {
 						apiSpec, err = openapi.ParseLenient(data)
 					} else {
 						apiSpec, err = openapi.Parse(data)
 					}
+				} else if graphql.IsGraphQLSDL(data) {
+					apiSpec, err = graphql.ParseSDLBytes(specFile, data)
 				} else {
 					apiSpec, err = spec.ParseBytes(data)
 				}
diff --git a/internal/pipeline/verify.go b/internal/pipeline/verify.go
index 3738ac15..c506de84 100644
--- a/internal/pipeline/verify.go
+++ b/internal/pipeline/verify.go
@@ -527,10 +527,25 @@ func countTableColumns(storeSrc, tableName string) int {
 
 func camelToKebab(s string) string {
 	var b strings.Builder
-	for i, r := range s {
+	runes := []rune(s)
+	for i, r := range runes {
 		if unicode.IsUpper(r) {
+			// Don't insert hyphen if:
+			// - first character
+			// - previous char was also uppercase AND next char is uppercase or end (acronym interior)
 			if i > 0 {
-				b.WriteByte('-')
+				prevUpper := unicode.IsUpper(runes[i-1])
+				nextUpper := i+1 < len(runes) && unicode.IsUpper(runes[i+1])
+				nextEnd := i+1 >= len(runes)
+				if prevUpper && (nextUpper || nextEnd) {
+					// Inside or at end of acronym - no hyphen
+				} else if !prevUpper {
+					// Start of new word after lowercase
+					b.WriteByte('-')
+				} else {
+					// prevUpper && next is lowercase = start of new word after acronym (e.g., PRTriage -> pr-triage)
+					b.WriteByte('-')
+				}
 			}
 			b.WriteRune(unicode.ToLower(r))
 		} else {

← 4f93e79f feat(verify): add runtime verification command with mock ser  ·  back to Cli Printing Press  ·  feat(skill): add product thesis, market research, naming pas ad4812f3 →