ISSUE-001-docs-navigation-naming
ISSUE-001: “Docs” Appearing as Navigation Name Instead of Repository Names
Status: ✅ RESOLVED
Priority: High
Created: 2025-12-19
Last Updated: 2025-12-19
Resolved: 2025-12-19
Additional Fix: 2025-12-19 (nested docs directories)
Problem Statement
The Hugo navigation sidebar shows “Docs” as the section name instead of the repository name. This creates confusing, nested “Docs > Docs > Docs” hierarchies instead of meaningful navigation like “Repository-Name > Documentation”.
Visual Evidence
Screenshot shows navigation structure:
Expected structure:
Root Cause Analysis (CONFIRMED)
The Problem
When a repository has a docs/ directory configured as the documentation path, the content structure becomes:
This causes Hugo navigation to show:
- Docs (the docs folder becomes a section)
- Docs (nested somehow by Hugo)
- ARCHITECTURE
- Docs (nested somehow by Hugo)
Root Cause
File: internal/hugo/pipeline/generators.go:generateSectionIndex()
Lines 135-142:
The logic has a isDocsBaseSection() check that SHOULD use the repo name when the section is “docs”, BUT there’s a problem with how it works.
The Bug: isDocsBaseSection() checks if all documents have DocsBase matching the section name. But when docs are in subdirectories like docs/architecture/file.md, the section is "docs" but the docsBase might be different paths, causing the check to fail.
Example Failure Case
Repository: franklin-hardpanel-mapper
Configured paths: ["docs"]
Files discovered:
docs/architecture.md→ section: “docs”, DocsBase: “docs” ✓docs/api/endpoint.md→ section: “docs/api”, DocsBase: “docs” ✗
When generating section index for “docs”:
isDocsBaseSection("docs", docs)checks all docs- Some docs have section “docs/api” not “docs”
- Check fails → uses
titleCase("docs")→ “Docs” instead of repo name
Investigation Plan
Phase 1: Trace Current Behavior ✅ COMPLETED
- Add debug logging to
generateSectionIndex()to see what title is being used - Check what
repoInfo.Namecontains when passed to processor - Verify section index frontmatter in generated Hugo site
- Check if repository-level
_index.mdis being created vs directory-level ones
Findings: Section indexes were using titleCase(filepath.Base(sectionName)) which converted “docs” → “Docs”. The isDocsBaseSection() check was failing for repositories with nested docs subdirectories.
Phase 2: Identify Title Source ✅ COMPLETED
- Find where section index title is determined
- Check if it’s using directory name vs repository name
- Verify
Document.RepositoryNameis populated correctly - Check if path parsing is extracting correct segment
Findings: Title determined in generators.go:generateSectionIndex() at line 138. Was using titleCase(repo) instead of repoMeta.Name, and isDocsBaseSection() logic was flawed for nested directories.
Phase 3: Fix Implementation ✅ COMPLETED
- Ensure repository-level
_index.mdusesrepo.Nameas title - Ensure subdirectory
_index.mdfiles use meaningful names (not “docs”) - Add test case for repository section title generation
- Update golden tests if output format changes
Actions Taken:
- Added
DocsPathsfield toRepositoryInfo - Replaced
isDocsBaseSection()withisConfiguredDocsPath() - Changed to use
repoMeta.Name(preserves original capitalization) - No golden test updates needed - existing tests validated the fix
Phase 4: Verification ✅ COMPLETED
- Run local build and inspect generated
_index.mdfiles - Check Hugo navigation in browser
- Run golden tests to ensure no regressions
- Document fix in this file
Results:
- Manual test:
franklin-hardpanel-mapper/docs/_index.md→ title: “Franklin Hardpanel Mapper” ✅ - All 16 golden integration tests pass ✅
- Full test suite passes (44 packages) ✅
- Linter clean (0 issues) ✅
Code Locations
Primary Files to Check
-
internal/hugo/pipeline/transform_section_indexes.go
Run()method - orchestrates section index generationgenerateSectionIndex()- creates individual_index.mdfiles- Title assignment logic
-
internal/hugo/pipeline/document.go
Document.RepositoryNamefieldRepositoryInfostructure
-
internal/hugo/pipeline/processor.go
- How
RepositoryInfois passed to transforms - Context available to section index generator
- How
-
internal/hugo/content_copy_pipeline.go
buildRepositoryMetadata()- where repo.Name should be set
Test Files
-
test/integration/golden_test.go
TestGolden_TwoRepos- multi-repo navigationTestGolden_SectionIndexes- section index generation
-
test/testdata/golden/*/content-structure.golden.json
- Expected frontmatter for
_index.mdfiles
- Expected frontmatter for
Previous Fix Attempts (Document History)
This Was The First Documented Attempt
The issue was tracked and resolved systematically on 2025-12-19.
Previous undocumented attempts (inferred from code history):
- The
isDocsBaseSection()function existed, suggesting at least one prior attempt to detect and handle docs directories - The logic checked all documents’
DocsBasefield, which was too fragile for nested directory structures - No documentation existed tracking why this approach was chosen or why it failed in production
Solution Strategy
The Fix (IMPLEMENTED APPROACH)
Root Cause: The isDocsBaseSection() function fails when documents are nested in subdirectories under “docs”.
Solution: Simplify the logic - ANY section that is a top-level documentation directory (matching the configured paths) should use the repository name as the title.
Implementation:
- Check if
sectionNameis in the repository’s configuredpaths(e.g., “docs”, “documentation”) - If yes → use repository name as title
- If no → use humanized directory name as title
Code Change Location: internal/hugo/pipeline/generators.go:generateSectionIndex()
Before:
After:
New helper function:
Why This Works
- Clear Identification: We know which directories are “docs roots” from configuration
- No Ambiguity: No need to inspect all documents’ metadata
- Correct Titles: Top-level docs directory gets repository name
- Nested Dirs: Subdirectories under docs/ get their own meaningful names
Example Results
Repository: franklin-hardpanel-mapper
Configured paths: ["docs"]
Generated sections:
content/franklin-hardpanel-mapper/docs/_index.md→ title: “Franklin Hardpanel Mapper” ✅content/franklin-hardpanel-mapper/docs/api/_index.md→ title: “API” ✅content/franklin-hardpanel-mapper/docs/architecture/_index.md→ title: “Architecture” ✅
Navigation shows:
NOT:
Resolution Summary
Date: 2025-12-19
Implementation (Initial Fix)
Modified 3 files:
-
internal/hugo/pipeline/document.go
- Added
DocsPaths []stringfield toRepositoryInfo - Stores all configured documentation paths from repository config
- Added
-
internal/hugo/content_copy_pipeline.go
- Populated
DocsPathswithrepo.Pathsfrom configuration - Default to
[]string{"docs"}when no paths configured
- Populated
-
internal/hugo/pipeline/generators.go
- Replaced
isDocsBaseSection()withisConfiguredDocsPath() - New function checks if section exactly matches a configured docs path
- When match found, uses
repoMeta.Name(repository name) instead oftitleCase(repo)or directory name
- Replaced
Additional Fix (Nested Docs Directories)
Problem Discovered: Repositories with nested directories matching the docs path name (e.g., docs/docs/docs/) still showed “Docs” in navigation.
Root Cause: The initial isConfiguredDocsPath() only checked for exact section match (e.g., “docs”), but didn’t handle cases where the section was “docs/docs” or “docs/docs/docs”.
Solution: Extended isConfiguredDocsPath() to also check if the last segment of the section path matches a configured docs path name.
Result: Any section ending with a docs path name uses the repository name as title.
Examples:
- Section
docs→ title: “Repository Name” ✅ - Section
docs/docs→ title: “Repository Name” ✅ - Section
docs/docs/docs→ title: “Repository Name” ✅ - Section
docs/api→ title: “API” ✅
Test Results
- ✅ All 16 golden tests pass
- ✅ Full test suite passes (44 packages)
- ✅ Linter clean (0 issues)
- ✅ Manual verification:
franklin-hardpanel-mapper/docs/→ title “Franklin Hardpanel Mapper” - ✅ Nested docs test:
docs/docs/docs/architecture.md→ all intermediate indexes use repository name
Before vs After
Before Fix:
After Fix:
Why The Old Code Failed
The previous isDocsBaseSection() function checked if all documents in a section had DocsBase matching the section name. This failed when:
- Documents were in subdirectories (e.g.,
docs/architecture/file.md) - Some sections were
docswhile others weredocs/api - The check required ALL docs to match, causing it to fail and fall back to using directory name “Docs”
Why The New Code Works
The new isConfiguredDocsPath() function:
- Directly checks if section matches a configured path from
repositories[].paths - No need to inspect document metadata
- Simple, reliable, and performant
- Uses actual repository name from
repoMeta.Name(preserves capitalization and formatting)
Testing Checklist
- Single repository builds correctly
- Multiple repositories each show distinct names
- Nested directories show meaningful names
- “docs” directory doesn’t appear in navigation
- Golden tests pass
- Manual browser testing confirms navigation
Success Criteria
- Navigation shows repository names at top level
- No “Docs” repeated entries in navigation
- Each repository’s documentation is clearly separated
- Subdirectories use meaningful names
- All tests pass
Notes
- This issue affects user experience significantly
- Navigation is critical for multi-repo documentation sites
- Solution must be robust and tested thoroughly
- Consider adding specific test case just for this navigation scenario
Related Files
internal/hugo/pipeline/transform_section_indexes.go- Section generationinternal/hugo/pipeline/document.go- Document metadatainternal/hugo/content_copy_pipeline.go- Repository metadatatest/integration/golden_test.go- Integration tests