From 1421ad5ce1e585abb21750ee675d7f3947be211e Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Sat, 23 May 2026 00:08:32 +0200 Subject: [PATCH] chore(docs): widen docs-consistency test, archive stale concept docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TestDocsConsistency walk only iterated [".", "guides", "reference", "analysis"] — concepts/ was silently invisible, which is why amazon-music-oauth.md slipped into the tree without a SUMMARY entry. Refactored to walk the entire docs/ tree, with a small dirsToSkip allow-list (_includes, archive, diagrams, images) for asset trees. New top-level narrative directories are picked up automatically; only asset dirs need an explicit entry. The wider walk surfaced six previously-hidden concepts/* files. Five older planning artefacts ("Enhanced State Management System", "Upstream Bose Service Simulation") moved into docs/archive/ where the dirsToSkip already excludes them; concepts/README.md renamed to upstream-service-simulation-overview.md since "README.md" inside archive/ would be misleading. Spotify Overview and Amazon Music OAuth are user-facing narrative docs and are now linked under Concepts in SUMMARY.md. Note: concepts/streborn-patterns.md is internal review notes (its own opening line says so) and is currently unlinked from SUMMARY.md; will be handled separately by the maintainer. Co-Authored-By: Claude Sonnet 4.6 --- docs/SUMMARY.md | 2 + .../implementation-plan.md | 0 .../implementation-roadmap.md | 0 .../technical-specification.md | 0 .../upstream-service-simulation-overview.md} | 0 .../upstream-service-simulation.md | 0 pkg/service/handlers/docs_consistency_test.go | 119 +++++++++++------- 7 files changed, 78 insertions(+), 43 deletions(-) rename docs/{concepts => archive}/implementation-plan.md (100%) rename docs/{concepts => archive}/implementation-roadmap.md (100%) rename docs/{concepts => archive}/technical-specification.md (100%) rename docs/{concepts/README.md => archive/upstream-service-simulation-overview.md} (100%) rename docs/{concepts => archive}/upstream-service-simulation.md (100%) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index e4cc98d..f0ab325 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -53,8 +53,10 @@ ## Concepts * [Request Recording](REQUEST_RECORDING_CONCEPT.md) +* [Spotify Overview](concepts/spotify-overview.md) * [Spotify Priming Strategy](concepts/spotify-priming-strategy.md) * [Spotify OAuth](concepts/spotify-oauth.md) +* [Amazon Music OAuth](concepts/amazon-music-oauth.md) * [Encrypted Export](concepts/ENCRYPTED-EXPORT.md) * [Diagnostic Export (Maintainer Setup)](DIAGNOSTIC-EXPORT.md) * [soundtouch-web Roadmap](soundtouch-web-roadmap.md) diff --git a/docs/concepts/implementation-plan.md b/docs/archive/implementation-plan.md similarity index 100% rename from docs/concepts/implementation-plan.md rename to docs/archive/implementation-plan.md diff --git a/docs/concepts/implementation-roadmap.md b/docs/archive/implementation-roadmap.md similarity index 100% rename from docs/concepts/implementation-roadmap.md rename to docs/archive/implementation-roadmap.md diff --git a/docs/concepts/technical-specification.md b/docs/archive/technical-specification.md similarity index 100% rename from docs/concepts/technical-specification.md rename to docs/archive/technical-specification.md diff --git a/docs/concepts/README.md b/docs/archive/upstream-service-simulation-overview.md similarity index 100% rename from docs/concepts/README.md rename to docs/archive/upstream-service-simulation-overview.md diff --git a/docs/concepts/upstream-service-simulation.md b/docs/archive/upstream-service-simulation.md similarity index 100% rename from docs/concepts/upstream-service-simulation.md rename to docs/archive/upstream-service-simulation.md diff --git a/pkg/service/handlers/docs_consistency_test.go b/pkg/service/handlers/docs_consistency_test.go index 0afd7d7..cb31928 100644 --- a/pkg/service/handlers/docs_consistency_test.go +++ b/pkg/service/handlers/docs_consistency_test.go @@ -9,6 +9,27 @@ import ( "testing" ) +// dirsToSkip names docs/ subdirectories whose contents are not meant +// to appear in SUMMARY.md. These are asset / partial / archive trees, +// not narrative documentation: +// +// - _includes : HTML partials consumed by the docs site renderer +// - archive : superseded plans / status reports kept for the +// record but deliberately unlinked +// - diagrams : Mermaid sources for embedded diagrams +// - images : binary assets + a directory README that explains them +// +// Adding a new top-level dir under docs/ does NOT require touching +// this list — only add the dir name here when its contents should +// stay out of SUMMARY.md by design. Individual file exclusions live +// in .docsignore instead. +var dirsToSkip = map[string]bool{ + "_includes": true, + "archive": true, + "diagrams": true, + "images": true, +} + func TestDocsConsistency(t *testing.T) { // Root of the project relative to this test file // The test runs in the directory of the package @@ -22,61 +43,73 @@ func TestDocsConsistency(t *testing.T) { } summaryText := string(summaryContent) - docsIgnore := readDocsIgnore(t, filepath.Join(projectRoot, ".docsignore")) - // List of directories to check - dirsToCheck := []string{".", "guides", "reference", "analysis"} + // Walk the entire docs tree. Directory-level exclusions live in + // dirsToSkip above (asset / archive trees); file-level exclusions + // live in .docsignore (individual narrative docs that are + // intentionally unlinked). New subdirectories are picked up + // automatically — this is the behaviour amazon-music-oauth.md + // surprised us by lacking. + err = filepath.WalkDir(docsDir, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } - for _, dir := range dirsToCheck { - dirPath := filepath.Join(docsDir, dir) - err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - // Don't recurse into subdirectories if we are checking the root, - // as they are handled separately or ignored (like archive) - if dir == "." && path != dirPath { - return filepath.SkipDir - } - return nil - } - if !strings.HasSuffix(d.Name(), ".md") { + if d.IsDir() { + if path == docsDir { return nil } - // Skip SUMMARY.md itself - if d.Name() == "SUMMARY.md" { - return nil + rel, relErr := filepath.Rel(docsDir, path) + if relErr != nil { + return relErr } - // Skip files listed in .docsignore at the project root - for _, skip := range docsIgnore { - if strings.HasSuffix(path, filepath.FromSlash(skip)) { - return nil - } - } - - // Get relative path from docs/ - relPath, err := filepath.Rel(docsDir, path) - if err != nil { - return err - } - - // Check if this file is linked in SUMMARY.md - // We look for [Label](relPath) - linkPattern := "(" + relPath + ")" - if !strings.Contains(summaryText, linkPattern) { - t.Errorf("Documentation file %s is not linked in docs/SUMMARY.md", relPath) + // Skip only top-level asset / archive directories. Nested + // directories inside narrative trees (e.g. docs/guides/foo/) + // would still be walked. + if !strings.ContainsRune(rel, filepath.Separator) && dirsToSkip[d.Name()] { + return filepath.SkipDir } return nil - }) - - if err != nil { - t.Errorf("Error walking directory %s: %v", dir, err) } + + if !strings.HasSuffix(d.Name(), ".md") { + return nil + } + + // Skip SUMMARY.md itself + if d.Name() == "SUMMARY.md" { + return nil + } + + // Skip files listed in .docsignore at the project root + for _, skip := range docsIgnore { + if strings.HasSuffix(path, filepath.FromSlash(skip)) { + return nil + } + } + + // Get relative path from docs/ + relPath, err := filepath.Rel(docsDir, path) + if err != nil { + return err + } + + // Check if this file is linked in SUMMARY.md + // We look for [Label](relPath) + linkPattern := "(" + relPath + ")" + if !strings.Contains(summaryText, linkPattern) { + t.Errorf("Documentation file %s is not linked in docs/SUMMARY.md", relPath) + } + + return nil + }) + + if err != nil { + t.Errorf("Error walking docs directory: %v", err) } }