Skip to content

Detect drift between API route files and endpoint docs#26

Open
ogero wants to merge 1 commit intodeepnoodle-ai:mainfrom
ogero:go/keep-api-spec-on-sync
Open

Detect drift between API route files and endpoint docs#26
ogero wants to merge 1 commit intodeepnoodle-ai:mainfrom
ogero:go/keep-api-spec-on-sync

Conversation

@ogero
Copy link
Copy Markdown

@ogero ogero commented Feb 12, 2026

Summary by CodeRabbit

  • Chores
    • Added automated validation to detect mismatches between API routes and documentation, identifying undocumented endpoints and outdated docs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

A new hook system is introduced to detect drift between API route definitions and documentation, automatically triggered when API routes or API docs are modified, using a Bash script that compares documented endpoints against implemented routes.

Changes

Cohort / File(s) Summary
Hook Script
.claude/hooks/api-doc-sync.sh
New Bash script that detects discrepancies between API route definitions (app/routes/api..ts) and documentation (docs/api/.md). Extracts route-to-URL mappings, compares against documented HTTP method endpoints, and outputs JSON with undocumented routes and stale documentation.
Hook Configuration
.claude/settings.json, .claude/settings.local.json
Adds PostToolUse hook configuration to trigger api-doc-sync.sh on Edit/Write tool actions, with accompanying permissions grants for bash, chmod, git checkout, and echo operations.

Sequence Diagram

sequenceDiagram
    participant Claude as Claude (Tool Usage)
    participant Hook as Hook System
    participant Script as api-doc-sync.sh
    participant Files as File System
    participant Output as JSON Message

    Claude->>Hook: Executes Edit/Write tool
    Hook->>Script: Triggers PostToolUse hook
    Script->>Files: Read API route files<br/>(app/routes/api.*.ts)
    Script->>Files: Read API documentation<br/>(docs/api/*.md)
    Script->>Script: Extract endpoints from routes<br/>(loader → GET, action → POST)
    Script->>Script: Extract endpoints from docs<br/>(### METHOD /path headings)
    Script->>Script: Normalize paths & compare
    Script->>Output: Generate drift report<br/>(undocumented routes &<br/>stale documentation)
    Output-->>Hook: Return JSON message
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Routes and docs forever in sync,
No drift shall slip through the brink,
Our hook watches files with care,
When API changes appear,
Truth and documentation will never unlink! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: introducing a hook to detect drift between API route files and endpoint documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines +1 to +10
{
"permissions": {
"allow": [
"Bash(chmod:*)",
"Bash(bash:*)",
"Bash(git checkout:*)",
"Bash(echo:*)"
]
}
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

😆 woops

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.claude/hooks/api-doc-sync.sh:
- Around line 40-48: The current method-detection in the hook uses a grep that
only finds negation guards and only takes the first match, so positive guards
like "request.method === 'PUT'" are missed and multi-method actions get
truncated; update the grep/regex logic in the block that sets ROUTE_ENDPOINTS
(the code that assigns to ROUTE_ENDPOINTS["$method $path"]=1 and the local
variable method) to: 1) match both equality and inequality comparisons (===, ==,
!==, !=) against request.method and capture the quoted method name(s), 2)
collect all matches instead of using head -1, and 3) when a negation operator is
detected (!= or !==) invert the logic to treat the captured method as the
accepted one, then iterate over all captured methods and add
ROUTE_ENDPOINTS["<METHOD> $path"]=1 for each.
🧹 Nitpick comments (2)
.claude/hooks/api-doc-sync.sh (1)

42-42: grep -P (PCRE) is not portable — unavailable on macOS default grep.

If contributors run this on macOS, grep -oP will fail. Consider using grep -oE with a revised regex, or using sed/awk instead.

.claude/settings.local.json (1)

3-8: Overly broad permissions — Bash(bash:*) allows arbitrary script execution.

bash:* permits running any script, not just the hook. Consider scoping it to the specific hook command. Also, git checkout:* doesn't seem required by the hook script — is it needed for another purpose?

Suggested tightening
   "permissions": {
     "allow": [
       "Bash(chmod:*)",
-      "Bash(bash:*)",
-      "Bash(git checkout:*)",
+      "Bash(bash:.claude/hooks/*)",
       "Bash(echo:*)"
     ]
   }

Comment on lines +40 to +48
if grep -q 'export async function action' "$route_file"; then
# Explicit method guard: request.method !== 'METHOD' → accepts METHOD
method=$(grep -oP "request\.method\s*!==?\s*'\K\w+" "$route_file" | head -1 || true)
if [[ -n "$method" ]]; then
ROUTE_ENDPOINTS["$method $path"]=1
else
# No guard → default action method is POST
ROUTE_ENDPOINTS["POST $path"]=1
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Method-detection heuristic is inverted for === guards and ignores multi-method actions.

The regex on line 42 matches only !== / != (negation guards). This works for the "reject if not X" pattern (if (request.method !== 'DELETE') throw … → accepts DELETE), but it will miss positive-match branches like if (request.method === 'PUT') { … }, silently defaulting those to POST. It also only captures the first method via head -1, so an action handling both PUT and DELETE would only register one.

Consider also matching === patterns and collecting all matched methods:

Proposed improvement
   # action export → determine HTTP method
   if grep -q 'export async function action' "$route_file"; then
-    # Explicit method guard: request.method !== 'METHOD' → accepts METHOD
-    method=$(grep -oP "request\.method\s*!==?\s*'\K\w+" "$route_file" | head -1 || true)
-    if [[ -n "$method" ]]; then
-      ROUTE_ENDPOINTS["$method $path"]=1
-    else
-      # No guard → default action method is POST
-      ROUTE_ENDPOINTS["POST $path"]=1
-    fi
+    # Collect methods from both === and !== guards
+    methods=$(grep -oP "request\.method\s*[!=]==?\s*'\K\w+" "$route_file" || true)
+    if [[ -n "$methods" ]]; then
+      while IFS= read -r method; do
+        ROUTE_ENDPOINTS["$method $path"]=1
+      done <<< "$methods"
+    else
+      # No guard → default action method is POST
+      ROUTE_ENDPOINTS["POST $path"]=1
+    fi
   fi
🤖 Prompt for AI Agents
In @.claude/hooks/api-doc-sync.sh around lines 40 - 48, The current
method-detection in the hook uses a grep that only finds negation guards and
only takes the first match, so positive guards like "request.method === 'PUT'"
are missed and multi-method actions get truncated; update the grep/regex logic
in the block that sets ROUTE_ENDPOINTS (the code that assigns to
ROUTE_ENDPOINTS["$method $path"]=1 and the local variable method) to: 1) match
both equality and inequality comparisons (===, ==, !==, !=) against
request.method and capture the quoted method name(s), 2) collect all matches
instead of using head -1, and 3) when a negation operator is detected (!= or
!==) invert the logic to treat the captured method as the accepted one, then
iterate over all captured methods and add ROUTE_ENDPOINTS["<METHOD> $path"]=1
for each.

@myzie
Copy link
Copy Markdown
Contributor

myzie commented Feb 12, 2026

Ah, nice one, thank you @ogero

Let me give this a try!

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.

2 participants