From 9e3b14586640951feea117d35c690eaf782f0292 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Mon, 27 Jul 2026 11:34:06 +0300 Subject: [PATCH] fix(wiki): reserve manual/jobs only at the wiki root, not at any depth is_reserved_page_path tested every directory component: any(part in RESERVED_WIKI_SOURCE_DIRS for part in relative.parts[:-1]) but the reserved directories are only ever created at the wiki root (WikiService: `almanac_path / "manual"`, BuildWorkflow: `almanac_path / "manual"`). Any user page under a directory named `manual` or `jobs`, at any depth, was therefore excluded from indexing, health and frontmatter rewriting. Silently, which is the costly part: health and validate walk with the same iterator, so the tool cannot report pages it refuses to see. The visible symptom is a broken link to a file that plainly exists on disk, plus a page count that disagrees with the filesystem. It also makes `sync` fail permanently, since the post-run validation gate trips on those broken links. In one real wiki this hid four committed pages (~29KB) under architecture/jobs/, and nearly caused a duplicate to be written: the coverage map listed the target as unwritten and health called the link broken, both consistent with "missing", when it existed and was hidden. Restricting the check to the first directory component keeps the intended exclusion (the bundled manual, including its subdirectories) while leaving user pages alone. page_id_for_path in the same module already reasons positionally, so top-level-only matches the established convention here. The added test fails without this change and passes with it. Root-level manual/ exclusion stays covered by the existing test. Refs #56 --- src/codealmanac/services/wiki/paths.py | 3 ++- tests/test_wiki_parsing.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/codealmanac/services/wiki/paths.py b/src/codealmanac/services/wiki/paths.py index 4fe21863..698706da 100644 --- a/src/codealmanac/services/wiki/paths.py +++ b/src/codealmanac/services/wiki/paths.py @@ -62,7 +62,8 @@ def is_reserved_page_path(almanac_path: Path, path: Path) -> bool: relative = path.relative_to(almanac_path) except ValueError: return True - return any(part in RESERVED_WIKI_SOURCE_DIRS for part in relative.parts[:-1]) + directories = relative.parts[:-1] + return bool(directories) and directories[0] in RESERVED_WIKI_SOURCE_DIRS def page_id_for_path(almanac_path: Path, page_path: Path) -> str: diff --git a/tests/test_wiki_parsing.py b/tests/test_wiki_parsing.py index 097f20a3..94c887fb 100644 --- a/tests/test_wiki_parsing.py +++ b/tests/test_wiki_parsing.py @@ -19,6 +19,23 @@ def test_page_iteration_excludes_repository_manuals(tmp_path): assert tuple(iter_page_paths(almanac_path)) == (page,) +def test_page_iteration_keeps_nested_dirs_named_after_reserved_roots(tmp_path): + almanac_path = tmp_path / "almanac" + reserved_manual = almanac_path / "manual" + reserved_manual.mkdir(parents=True) + (reserved_manual / "how-to-write.md").write_text("# Manual\n", encoding="utf-8") + + nested_jobs = almanac_path / "architecture/jobs/queue.md" + nested_jobs.parent.mkdir(parents=True) + nested_jobs.write_text("# Queue\n", encoding="utf-8") + + nested_manual = almanac_path / "guides/manual/setup.md" + nested_manual.parent.mkdir(parents=True) + nested_manual.write_text("# Setup\n", encoding="utf-8") + + assert tuple(iter_page_paths(almanac_path)) == (nested_jobs, nested_manual) + + def test_frontmatter_uses_pydantic_validated_shape(): parsed = parse_frontmatter( """---