PROJECT NAME PATH DUPLICATION FIX

Comprehensive documentation system guides

Project Name Path Duplication Fix

Date: October 29, 2025
Time: Evening Session
Status: ✅ FIXED
Purpose: Resolve path duplication issue where project name was being included twice in file paths


🐛 The Problem

Error Symptoms

HEAD http://localhost:5010/docs/english/Architecture/Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE.mdc 429 (Too Many Requests)

Root Cause

In the project-scoped architecture, URLs include the project name as the first segment:

However, PathResolutionService.getEffectiveDocsPath() already includes the project name in the base path:

When MarkdownService.generatePossiblePaths() was called with the full path including project name, it resulted in:

This caused:

  1. 404 errors for non-existent files
  2. Circuit breaker activation due to too many failed requests
  3. Content not rendering because files couldn't be loaded

✅ The Solution

Implementation

Modified three methods in MarkdownService to strip the project name from the path before calling generatePossiblePaths():

  1. loadContent() - Regular content loading
  2. detectFileType() - File type detection (.md vs .mdc)
  3. loadMDCContent() - MDC-specific content loading

File: apps/docs-angular/src/app/services/markdown.service.ts

loadContent(path: string, language: 'en' | 'es' = 'en'): Observable<MarkdownContent> {
  // Remove leading slash if present
  let cleanPath = path.startsWith('/') ? path.slice(1) : path;

  // ... (special route checks) ...

  // 🎯 CRITICAL FIX: Strip project name from path to prevent duplication
  // In project-scoped architecture, URLs include project name (e.g., /Architecture/ecosystem/file)
  // But getEffectiveDocsPath() already includes project name in base path (e.g., docs/english/Architecture)
  // So we must strip project name from cleanPath before calling generatePossiblePaths
  const currentProject = this.store.selectSnapshot(ProjectSelectionState.currentProjectMetadata);
  if (currentProject && cleanPath.startsWith(`${currentProject.name}/`)) {
    const pathWithoutProject = cleanPath.substring(currentProject.name.length + 1);
    console.log(`🔧 Stripped project name from path: "${cleanPath}" → "${pathWithoutProject}"`);
    cleanPath = pathWithoutProject;
  }

  // ... (rest of the method) ...
}

// 🎯 SAME FIX IN detectFileType()
private detectFileType(path: string, language: string): Observable<'md' | 'mdc'> {
  let cleanPath = path.startsWith('/') ? path.slice(1) : path;

  // 🎯 CRITICAL FIX: Strip project name from path to prevent duplication
  // (Same logic as in loadContent method)
  const currentProject = this.store.selectSnapshot(ProjectSelectionState.currentProjectMetadata);
  if (currentProject && cleanPath.startsWith(`${currentProject.name}/`)) {
    const pathWithoutProject = cleanPath.substring(currentProject.name.length + 1);
    console.log(`🔧 detectFileType: Stripped project name from path: "${cleanPath}" → "${pathWithoutProject}"`);
    cleanPath = pathWithoutProject;
  }

  // ... (rest of the method) ...
}

// 🎯 SAME FIX IN loadMDCContent()
private loadMDCContent(path: string, language: string): Observable<MDCContentResult> {
  let cleanPath = path.startsWith('/') ? path.slice(1) : path;

  // 🎯 CRITICAL FIX: Strip project name from path to prevent duplication
  // (Same logic as in loadContent and detectFileType methods)
  const currentProject = this.store.selectSnapshot(ProjectSelectionState.currentProjectMetadata);
  if (currentProject && cleanPath.startsWith(`${currentProject.name}/`)) {
    const pathWithoutProject = cleanPath.substring(currentProject.name.length + 1);
    console.log(`🔧 loadMDCContent: Stripped project name from path: "${cleanPath}" → "${pathWithoutProject}"`);
    cleanPath = pathWithoutProject;
  }

  // ... (rest of the method) ...
}

Why All Three Methods Need the Fix

The path resolution happens in three different code paths, each calling generatePossiblePaths():

  1. loadContent() - Called when loading regular Markdown files
  2. detectFileType() - Called independently to determine file extension before loading
  3. loadMDCContent() - Called specifically when loading .mdc files (component-rich documentation)

All three must strip the project name because they all call generatePossiblePaths(), which combines the path with the already-project-scoped basePath.

How It Works

  1. URL Parsing: /Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE → Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
  2. Project Detection: Get current project metadata from NGXS store → { name: 'Architecture', ... }
  3. Project Name Stripping: Check if path starts with Architecture/ → strip it → ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
  4. Path Resolution: docs/english/Architecture + ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE = docs/english/Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE ✅

Before and After

Before (❌ Duplicated):

URL: /Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
↓
cleanPath: Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
↓
basePath: docs/english/Architecture
↓
finalPath: docs/english/Architecture/Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE ❌

After (✅ Correct):

URL: /Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
↓
cleanPath: Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
↓
Strip project name: ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
↓
basePath: docs/english/Architecture
↓
finalPath: docs/english/Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE ✅

🧪 Testing

Expected Results

  1. ✅ No path duplication in file requests
  2. ✅ Files load correctly from proper paths
  3. ✅ No circuit breaker activations
  4. ✅ Content renders successfully
  5. ✅ Console logs show correct path stripping in all three methods:
    • 🔧 Stripped project name from path: "Architecture/..." → "..."
    • 🔧 detectFileType: Stripped project name from path: "Architecture/..." → "..."
    • 🔧 loadMDCContent: Stripped project name from path: "Architecture/..." → "..."

Test Cases

// Test 1: Architecture project file
URL: /Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE
Expected: docs/english/Architecture/ecosystem/ECOSYSTEM-INTEGRATION-COMPLETE.mdc ✅

// Test 2: GoGrab-Space project file
URL: /GoGrab-Space/libs/icons/index
Expected: docs/english/GoGrab-Space/libs/icons/index.mdc ✅

// Test 3: Public project file
URL: /getting-started
Expected: docs/english/public/getting-started.md ✅

// Test 4: Root index
URL: /Architecture
Expected: docs/english/Architecture/index.mdc ✅

🔗 Related Issues

Previous Fix Attempts

  1. Path Duplication Fix v1 (INFINITE_LOOP_PATH_DUPLICATION_FIX.md): Fixed duplication in SearchService during indexing
  2. Extension Detection Fix (LOADCONTENT_EXTENSION_DETECTION_FIX.md): Fixed .md vs .mdc extension handling
  3. Circuit Breaker Fix (CIRCUIT_BREAKER_FIX.md): Added emergency circuit breaker for runaway requests

Why This Fix Was Needed

The previous fixes addressed search indexing and file type detection, but didn't address the root cause in MarkdownService.loadContent() where the path was being passed with the project name included.

This fix is the final piece of the path resolution puzzle, ensuring that:


📚 Architecture Context

Project-Scoped URL Architecture

In the new architecture:

Key Insight

URLs and base paths both include the project name, so we must strip it from the URL path before combining with the base path.


🎯 Impact

Before Fix

After Fix


📝 Next Steps

  1. Hard refresh browser to clear caches
  2. Reset circuit breaker if needed (see below)
  3. Navigate to Architecture project file
  4. Verify console logs show path stripping: 🔧 Stripped project name from path: "Architecture/..." → "..."
  5. Verify file loads successfully with correct path
  6. Test other projects (GoGrab-Space, GoGrab-Pulse, etc.)

🔧 Circuit Breaker Reset (if needed)

If you see Status: 429 (Too Many Requests) errors, the circuit breaker is blocking requests due to previous failures. To reset it:

In Browser Console:

window.resetCircuitBreaker()

This will clear all circuit breaker counters and request caches, allowing requests to proceed normally.


✅ Status

COMPLETE - This fix resolves the path duplication issue in the content loading pipeline by applying the same path stripping logic to all three content loading methods.

NOTE: This fix was necessary but not sufficient on its own. After implementing this fix, we discovered that HTTP HEAD requests were being rate-limited (429 errors). The final solution was to use manifest-based file type detection instead of HTTP requests. See MANIFEST_BASED_FILE_TYPE_DETECTION.md for the complete solution.

Implementation Details